home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1996-05-22 | 236.9 KB | 6,550 lines
// HELPDECO - Utility-Programm zum Zerlegen von Windows Hilfedateien // HELPDECO - utility program to dissect Windows help files // // HELPDECO zerlegt eine HLP-Datei von Windows 3.0, 3.1, 3.11 und '95 und // viele MVB-Dateien des Multimedia-Viewers in alle für den jeweiligen // Hilfecompiler HC30, HC31, HCP und HCW bzw. Multimediacompiler WMVC12 // oder MVC zum erneuten Zusammenbau erforderlichen Dateien. Dazu gehören: // HPJ - die Projektdatei, als Parameter für den Hilfecompiler anzugeben // MVP - die Multimediaprojektdatei, als Parameter für den MM-Compiler // RTF - die Textdatei mit dem gesamten Hilfetext und allen Fußnoten // PH - die Phrasen-Datei (wie sie auch vom Hilfecompiler erzeugt wird) // ICO - ein eventuell der Hilfedatei zugeordnetes Icon // BMP/WMF/SHG/MRB - alle Bilder in Dateien mit passendem Format // Baggage - alle als Baggage in der Hilfedatei enthaltenen Dateien // // HELPDECO dissects Windows 3.0, 3.1, 3.11 und '95 HLP files and many // multi media viewer MVB files into all files required for a rebuild // using HC30, HC31, HCP, and HCW or multi media compilers WMVC12 and MVC // HPJ - help project file, use as parameter when calling help compiler // MVP - multi media project file, parameter for multi media compiler // RTF - text file containing whole content of help file and all footnotes // PH - phrases file (same as produced by help compiler) // ICO - icon of help file if supplied // BMP/WMF/SHG/MRB - embedded pictures in appropriate format // Baggage - all baggage files contained in help file // // HELPDECO wird von der MS-DOS Kommandozeile aus mit dem Namen der zu // bearbeitenden Datei, eventuell dem Namen einer internen Datei und // eventuellen Optionen aufgerufen: // Call HELPDECO from MS-DOS command line. Supply name of help file // to use, optional name of internal file, and options if appropriate. // // HELPDECO // Zeigt Benutzungshinweise // Displays usage // // HELPDECO helpfilename // Zerlegt die Hilfedatei in alle zum erneuten Zusammenbau benötigten // Dateien. Diese Dateien werden im aktuellen (möglichst leeren) // Verzeichnis abgelegt. Existierende Dateien werden ohne Rückfrage // überschrieben wenn die Option /y angegeben wird. // Decompiles help file into all sources needed for rebuild. All files // are created in current directory (should be empty). Existing files // will be overwritten without asking if option /y was specified. // // Options: /m kann verwendet werden, um das Durchsuchen von macros // nach Topicnamen zu verhindern, wenn dabei Probleme // auftreten. Hilfecompiler wird Warnung 4131 melden. // May be used to stop parsing macros for topic names. // Help compiler will emit Warning 4131. // /b kann verwendet werden, um das Auflösen von Browse- // Sequenzen zu verhindern, wenn dabei Probleme auftreten. // Hilfequelltextdatei enthält dann keine +-Fußnoten. // May be used to stop resolving browse sequences. Help // source file than contains no + footnotes. // /w Erlaubt die Anzeige von Warnungen, die die Meldung 'HELPDECO // had problems with' erklären. // Enables display of warnings explaining message 'HELPDECO // had problems with'. // // HELPDECO helpfilename /r // Erzeugt aus der Hilfedatei eine RTF-Datei, die von WinWord geladen // dasselbe Aussehen hat wie die von WinHelp angezeigten Hilfeseiten. // Damit kann eine Hilfedatei komplett gedruckt oder weiterverarbeitet // werden. // Converts help file into RTF file of same appearance if loaded into // WinWord as if displayed by WinHelp. To print or work with complete // content. // // HELPDECO helpfilename /c // Erzeugt aus der Hilfedatei eine *.CNT-Datei für WinHlp32, die alle // Kapitel mit Überschriften in der Reihenfolge enthält, in der sie in // der Hilfedatei auftreten. Die Datei muß dann mit HCW 4.00 oder einem // Texteditor in eine hierarchische Struktur überarbeitet werden. // Generates a *.CNT file used by WinHlp32, containing all chapters that // have titles assigned in the order they appear in the helpfile. This // file should then be edited using HCW 4.00 or any text editor into a // hierarchical order. // // HELPDECO helpfilename /p // Prüft Referenzen auf externe Hilfedateien. // Checks references to external help files. // // HELPDECO helpfilename /d // Zeigt das interne Inhaltsverzeichnis der Hilfedatei // Displays internal directory of help file // // HELPDECO helpfilename /x // Zeigt das interne Inhaltsverzeichnis als HexDump // Displays hex dump of internal directory // // HELPDECO helpfilename "internalfilename" // Zeigt die genannte interne Datei in einem passenden Format an, soweit // die interne Datei anzeigbar ist, sonst als HexDump // Displays internal file in appropriate format if known, else hex dump // // HELPDECO helpfilename "internalfilename" /x // Zeigt die genannte interne Datei als HexDump // Displays hex dump of internal file // // HELPDECO helpfilename /t // Zeigt farbcodierten HexDump der |TOPIC-Datei // Displays color coded hex dump of |TOPIC file // // HELPDECO wurde erstellt von / was written by // Manfred Winterhoff, Geschw.-Scholl-Ring 17, 38444 Wolfsburg, Germany // CIS 100326,2776 // // Informieren Sie mich, wenn Sie HELPDECO modifizieren oder erweitern um // mehr Features und größere Hilfedateien zu bearbeiten. // Please give me a note if you modify HELPDECO to handle more formats or // bigger help files. // // HELPDECO basiert auf HELPDUMP von Pete Davis veröffentlicht in: // HELPDECO is based upon HELPDUMP from Pete Davis published in: // The Windows Help File Format, Dr. Dobbs Journal, Sep/Oct 1993 // // Die neueste Version von HELPDECO befindet sich stets in: // The newest public version of HELPDECO is always available at: // CompuServe Dr. Dobbs Journal DDJFOR Undocumented Corner HELPDCxx.ZIP // // HELPDECO ist public domain Software. Der Einsatz erfolgt auf eigene // Gefahr. Kein Programmteil darf kommerziell verwendet werden. Für das // Kopieren dürfen keine Gebühren verlangt werden (Sharewarehandel // Finger weg). Immer auch die Quelltexte weitergeben, da es für einige // Hilfedateien erforderlich sein kann, das Programm zu verändern. // HELPDECO is donated to the public domain. Use at your own risk. No // part of the program may be used commercially. No fees may be charged // on distributing the program (shareware distributors keep off). // Always distribute with source as it may be neccessary to modify the // program to handle certain help files. // // Version 1.7 // removed unneccessary output statement // // Version 1.6 can now check references to external help files plus: // duplicate macro names preceeding picture hotspot info skipped // does not write Win95 commands to multi-media help project files // changed unhash to circumvent Microsoft-C++ float rounding error // handles keywords defined inside topic text // // Version 1.5 // fixed static on buffer of TopicName function (affected HC30 files) // // Version 1.4 fixes some bugs reported by different users: // buffer overflow in expanding LZ77&RunLen (byPacked 3) images fixed // embedded images {bmxwd} larger than 32k supported // extract topic names from jump into external file if no file specified // handles more phrases on HCW 4.00 (Win95) help files // Windows 3.1 (HC31) |Phrases always Zeck compressed // LinkData2 buffer enlarged 1 byte to store trailing NUL character // // Version 1.3 // parses examples of {bmc} etc. statements contained in help text correctly // can now generate a *.CNT content file for Windows 95 / WinHlp32 // Microsoft C: ctype macros (isalnum/isprint) don't work with signed char // // Version 1.2 fixes some severe bugs introduced in version 1.1 and: // tells you which help compiler to use // collects multiple keyword footnotes into single lines // handles \r\n in COPYRIGHT // converts SPC-macro (but only in [CONFIG] section) // does not generate duplicate MAP-statements if possible // {button} and {mci,mci_left,mci_right} commands supported // [BITMAP]-section in HCW 4.00 help files irritated transparent bitmaps // // Version 1.1 now supports more features of Win95/HCW 4.00/WinHlp32: // Supports LCID, CHARSET, AUTO-SIZE HEIGHT, CNT, INDEX_SEPARATORS // Additional Win95 Macros (to extract original topic names) // [CONFIG:n] of Win95 supported (internal file |CFn) // Secondary windows with > footnote supported (internal file |VIOLA) // Transparent bitmaps supported (bmct,bmlt,bmrt) // Expanded internal limits as HCW 4.00 allows larger items // Now does RunLen compressed device dependend bitmaps // Bugs in handling of metafiles removed // Bug in placement of pack(1) removed // Parsing of macros changed (is it really better now ?) // // HELPDECO wurde mit über 500 Hilfedateien getestet. Aber einige gehen nicht // HELPDECO was tested with more than 500 help files. But some don't work // PRINTMAN.HLP 50.743 03/10/92 3:10 (Hilfe zum Druck-Manager) corrupt // BACKSDK.MVB 9.367.750 08/09/95 13:30 8b/8c unknown // bei anderen treten beim Dekompilieren Probleme auf // others exhibit problems during decompilation // ISQLW.HLP 693.763 11/16/94 12:00 (Watcom SQL) // WSQLODBC.HLP 14.995 11/16/94 12:00 (Watcom SQL ODBC) // FONTEDIT.HLP 16.124 03/10/95 12:00 or 03/23/92 3:10 // UNIDRV.HLP 45.107 31/12/93 3:11 // WIN31WH.HLP 3.386.753 11/17/94 4:50 (Windows 3.1 SDK) // und einige Hilfedateien sind einfach zu groß wie // and certain help files are simply too big, like // DELPHI.HLP 4.765.203 02/17/95 8:00 (DELPHI Hilfe) // WIN32.HLP 21.992.070 07/11/95 9:50 (Win32 SDK) // #include <time.h> #include <malloc.h> #include <stdio.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> #include <conio.h> #include <ctype.h> #include <limits.h> #include <math.h> // compile in large memory model if large help files should be handled // neccessary compiler options using Microsoft C/C++: // cl -AL -Os -J -F 1000 helpdeco.c // neccessary compiler options using Borland C/C++: // bcc -ml -K helpdeco.c // You may try to compile with 32-bit compiler to handle even larger // help files, but I don't have one, so it may need changes. // byte align ! #ifdef __TURBOC__ typedef struct { char a,b,c; } align; #if sizeof(align)!=3 #error Compile bytealigned #endif #else #pragma pack(1) #endif typedef struct HELPHEADER // structure at beginning of help file { unsigned short Magic; // 0x5F3F unsigned short Version; // 0x0003 long DirectoryStart; // offset of FILEHEADER of internal direcory long Negative1; // -1L long EntireFileSize; // size of entire help file in bytes } HELPHEADER; typedef struct FILEHEADER // structure at FileOffset of each internal file { long FileSizePlus9; // size of internal file including FILEHEADER long FileSize; // size of internal file in bytes char FileFlags; // normally 0 } FILEHEADER; typedef struct BTREEHEADER // structure after FILEHEADER of each Btree { unsigned short Magic; // 0x293B unsigned short Flags; // bit 0x0002 always 1, bit 0x0400 1 if direcory unsigned short PageSize; // 0x0400=1k if directory, 0x0800=2k else char Unknown[16]; short MustBeZero; // 0 short PageSplits; // number of page splits Btree has suffered short RootPage; // page number of Btree root page short MustBeNegOne; // 0xFFFF short TotalPages; // number of Btree pages short NLevels; // number of levels of Btree long TotalBtreeEntries; // number of entries in Btree } BTREEHEADER; typedef struct BTREEINDEXHEADER // structure at beginning of every index-page { unsigned short Unknown; // sorry, no ID to identify an index-page short NEntries; // number of entries in this index-page short PreviousPage; // page number of previous page } BTREEINDEXHEADER; typedef struct BTREENODEHEADER // structure at beginning of every leaf-page { unsigned short Unknown; // Sorry, no ID to identify a leaf-oage short NEntries; // number of entires in this leaf-page short PreviousPage; // page number of preceeding leaf-page or -1 short NextPage; // page number of next leaf-page or -1 } BTREENODEHEADER; typedef struct SYSTEMHEADER // structure at beginning of |SYSTEM file { unsigned char Magic; // 0x6C unsigned char Version; // version # always 3 unsigned char Revision; // revision code unsigned char Always0; unsigned short Always1; time_t GenDate; // date/time that the help file was generated or 0 unsigned short Flags; // tells you how the help file is compressed } SYSTEMHEADER; typedef struct SYSTEMREC // structure following version 3.1 SYSTEMHEADER { unsigned short RecordType; // type of data in record unsigned short DataSize; // size of data } SYSTEMREC; typedef struct SECWINDOW // structure of data of following RecordType 6 { unsigned short Flags; // flags (See Below) char Type[10]; // type of window char Name[9]; // window name char Caption[51]; // caption for window short X; // x coordinate of windows (0..1000) short Y; // y coordinate of window (0..1000) short Width; // width of windows (0..1000) short Height; // height of windows (0..1000) short Maximize; // maximize flag and window styles unsigned char Rgb[3]; // color of scrollable region unsigned char Unknown1; unsigned char RgbNsr[3]; // color of non-scrollable region unsigned char Unknown2; } SECWINDOW; typedef struct // structure of data following RecordType 14 { char btreename[10]; char mapname[10]; char dataname[10]; char title[80]; } KEYINDEX; #define WSYSFLAG_TYPE 0x0001 // Type is valid #define WSYSFLAG_NAME 0x0002 // Name is valid #define WSYSFLAG_CAPTION 0x0004 // Caption is valid #define WSYSFLAG_X 0x0008 // X is valid #define WSYSFLAG_Y 0x0010 // Y is valid #define WSYSFLAG_WIDTH 0x0020 // Width is valid #define WSYSFLAG_HEIGHT 0x0040 // Height is valid #define WSYSFLAG_MAXIMIZE 0x0080 // Maximize is valid #define WSYSFLAG_RGB 0x0100 // Rgb is valid #define WSYSFLAG_RGBNSR 0x0200 // RgbNsr is valid #define WSYSFLAG_TOP 0x0400 // On top was set in HPJ file #define WSYSFLAG_AUTOSIZEHEIGHT 0x0800 // Auto-Size Height typedef struct PHRINDEXHDR // structure of beginning of |PhrIndex file { long always4A01; // sometimes 0x0001 long entries; // number of phrases long compressedsize; // size of PhrIndex file long phrimagesize; // size of decompressed PhrImage file long phrimagecompressedsize; // size of PhrImage file long always0; unsigned short bits:4; unsigned short unknown:12; unsigned short always4A00; // sometimes 0x4A01, 0x4A02 } PHRINDEXHDR; typedef struct FONTHEADER // structure of beginning of |FONT file { unsigned short NumFacenames; // number of face names unsigned short NumDescriptors; // number of font descriptors unsigned short FacenamesOffset; // offset of face name array unsigned short DescriptorsOffset; // offset of descriptors array unsigned short NumFormats; // only if FacenamesOffset >= 12 unsigned short FormatsOffset; // offset of formats array } FONTHEADER; typedef struct FONTDESCRIPTOR // structure located at DescriptorsOffset { unsigned char Attributes; // Font Attributes See values below unsigned char HalfPoints; // PointSize * 2 unsigned char FontFamily; // Font Family. See values below unsigned short FontName; // Number of newfont in Font List unsigned char FGRGB[3]; // RGB values of foreground unsigned char BGRGB[3]; // unused background RGB Values } FONTDESCRIPTOR; typedef struct NEWFONT // structure located at DescriptoresOffset { unsigned char unknown1; short FontName; unsigned char FGRGB[3]; unsigned char BGRGB[3]; unsigned char unknown5; unsigned char unknown6; unsigned char unknown7; unsigned char unknown8; unsigned char unknown9; long Height; unsigned char mostlyzero[12]; short Weight; unsigned char unknown10; unsigned char unknown11; unsigned char Italic; unsigned char Underline; unsigned char StrikeOut; unsigned char DoubleUnderline; unsigned char SmallCaps; unsigned char unknown17; unsigned char unknown18; unsigned char PitchAndFamily; } NEWFONT; // Font Attributes #define FONT_NORM 0x00 // Normal #define FONT_BOLD 0x01 // Bold #define FONT_ITAL 0x02 // Italics #define FONT_UNDR 0x04 // Underline #define FONT_STRK 0x08 // Strike Through #define FONT_DBUN 0x10 // Dbl Underline #define FONT_SMCP 0x20 // Small Caps // Font Families #define FAM_MODERN 0x01 #define FAM_ROMAN 0x02 #define FAM_SWISS 0x03 #define FAM_TECH 0x03 #define FAM_NIL 0x03 #define FAM_SCRIPT 0x04 #define FAM_DECOR 0x05 typedef struct KWMAPREC // structure of |xWMAP leaf-page entries { long FirstRec; // index number of first keyword on leaf page unsigned short PageNum; // page number that keywords are associated with } KWMAPREC; typedef struct TOPICBLOCKHEADER // structure every TopicBlockSize of |TOPIC { long LastTopicLink; // offset of last topic link in previous block long TopicData; // offset of topic data start long LastTopicHeader; // offset of last topic header in previous block } TOPICBLOCKHEADER; typedef struct TOPICLINK // structure pointed to be TopicData { long BlockSize; // size of this link + LinkData1 + LinkData2 long DataLen2; // length of decompressed LinkData2 long PrevBlock; // relative to first byte of |TOPIC long NextBlock; // relative to first byte of |TOPIC long DataLen1; // includes size of TOPICLINK unsigned char RecordType; // See below } TOPICLINK; // Known RecordTypes for TOPICLINK #define TL_DISPLAY30 0x01 // version 3.0 displayable information #define TL_TOPICHDR 0x02 // topic header information #define TL_DISPLAY 0x20 // version 3.1 displayable information #define TL_TABLE 0x23 // version 3.1 table typedef struct TOPICHEADER // structure of LinkData1 of RecordType 2 { long BlockSize; // size of topic, including internal topic links long BrowseBck; // topic offset for prev topic in browse sequence long BrowseFor; // topic offset for next topic in browse sequence long TopicNum; // topic Number long NonScroll; // start of non-scrolling region (topic offset) or -1 long Scroll; // start of scrolling region (topic offset) long NextTopic; // start of next type 2 record } TOPICHEADER; typedef struct TOPICHEADER30 // structure of LinkData1 of RecordType 2 { long BlockSize; short PrevTopicNum; short unused1; short NextTopicNum; short unused2; } TOPICHEADER30; typedef struct CTXOMAPREC // structure of |CTXOMAP file entries { long MapID; long TopicOffset; } CTXOMAPREC; typedef struct CONTEXTREC // structure of |CONTEXT index-page entry { long HashValue; // Hash value of Topic Name long TopicOffset; // Topic offset } CONTEXTREC; typedef struct tagBITMAPFILEHEADER { unsigned short bfType; unsigned long bfSize; unsigned short bfReserved1; unsigned short bfReserved2; unsigned long bfOffBits; } BITMAPFILEHEADER; typedef struct tagBITMAPINFOHEADER { unsigned long biSize; long biWidth; long biHeight; unsigned short biPlanes; unsigned short biBitCount; unsigned long biCompression; unsigned long biSizeImage; long biXPelsPerMeter; long biYPelsPerMeter; unsigned long biClrUsed; unsigned long biClrImportant; } BITMAPINFOHEADER; typedef struct tagRECT { short left; short top; short right; short bottom; } RECT; typedef struct tagAPMFILEHEADER { unsigned long dwKey; unsigned short hMF; RECT rcBBox; unsigned short wInch; unsigned long dwReserved; unsigned short wChecksum; } APMFILEHEADER; typedef struct { unsigned char id0,id1,id2; unsigned short x,y,w,h; unsigned long hash; } HOTSPOT; typedef enum {FALSE,TRUE} BOOL; typedef struct BUFFER // structure used as buf of GetFirstPage { long FirstLeaf; unsigned short PageSize; short NextPage; } BUFFER; typedef struct browse // internal use. max. 3640 / 64k { long StartTopic; long NextTopic; long PrevTopic; short BrowseNum; short Start; short Count; } BROWSE; typedef struct start // internal use. max. 8191 / 64k { long StartTopic; short BrowseNum; short Start; } START; typedef struct hashrec // internal use. max. 8191 / 64k { char *name; long hash; } HASHREC; #define MAGIC 0x5774 typedef struct { int magic; char *ptr; char *end; } MFILE; char drive[_MAX_DRIVE]; char dir[_MAX_DIR]; char name[_MAX_FNAME]; char ext[_MAX_EXT]; HELPHEADER HelpHeader; HASHREC *hashrec; BROWSE *browse; int scaling=10; int browses; int browsenums; START *start; int starts; int hashrecs=0; BOOL before31; BOOL warn=FALSE; BOOL warnings=FALSE; int missing=0; BOOL lzcompressed; long *Topic; int Topics; // max. 16348 Topics CONTEXTREC *ContextRec; BOOL overwrite=FALSE; BOOL extractmacros=TRUE; BOOL resolvebrowse=TRUE; BOOL checkexternal=FALSE; char helpcomp[10]; char HelpFileTitle[51]; int ContextRecs; // max. 8191 Context Records long TopicFileStart=0L; SYSTEMHEADER SysHeader; // Global System Header Record unsigned short *Offsets; unsigned int PhraseCount; int TopicUse; int TopicBlockSize; // 2k or 4k int DecompressSize; // 4k or 16k unsigned char buffer[0x4000]; char index_separators[40]=",;"; int extensions=0; char *bmpext[]={"???","BMP","SHG","MRB","WMF","BMP","SHG"}; BOOL mvp; char *extension; int fontnames; char **fontname; long TopicPos; long TopicOffset; long TopicSize; int fonts; char *NewPhrases; FONTDESCRIPTOR *font; NEWFONT *newfont; int newfonts; BOOL dontCount; char **secondarywindownames; // Array NULL terminated BOOL NotInAnyTopic; BOOL lists['z'-'A'+1]; BOOL keyindex['z'-'A'+1]; unsigned char table[256]; void error(char *format,...) { va_list arg; va_start(arg,format); vfprintf(stderr,format,arg); va_end(arg); fprintf(stderr,"Press CR to continue at your own risk, any other key to exit.\n"); if(getch()!='\r') exit(1); } long myFTell(MFILE *f) { if(f->magic!=MAGIC) return ftell((FILE *)f); return (long)f->ptr; } void myFSeek(MFILE *f,long offset) { if(f->magic!=MAGIC) { fseek((FILE *)f,offset,SEEK_SET); } else { f->ptr=(char *)offset; } } void *myMalloc(long bytes) // save malloc function { void *ptr; if(bytes<1L||((size_t)bytes!=bytes)||(ptr=malloc((size_t)bytes))==NULL) { fprintf(stderr,"Allocation of %ld bytes failed. File too big.\n",bytes); exit(1); } return ptr; } void *myReAlloc(void *ptr,long bytes) // save realloc function { if(!ptr) return myMalloc(bytes); if(bytes<1L||bytes!=(size_t)bytes||(ptr=realloc(ptr,(size_t)bytes))==NULL) { fprintf(stderr,"Reallocation to %ld bytes failed. File too big.\n",bytes); exit(1); } return ptr; } char *myStrDup(char *ptr) // save strdup function { size_t len; char *dup; if(!ptr) return NULL; len=strlen(ptr); dup=myMalloc(len+1L); strcpy(dup,ptr); return dup; } long hash(char *name) // convert 3.1 topic name to hash value { long hash; unsigned char *ptr; if(*name=='\0') return 1L; for(hash=0L,ptr=(unsigned char *)name;*ptr;ptr++) { if(table[*ptr]==0) { fprintf(stderr,"Illegal char %c in Topic Name %s\n",*ptr,name); warnings=TRUE; return 0L; } hash=(hash*0x2BU)+table[*ptr]; } return hash; } char *unhash(unsigned long hash) // deliver 3.1 topic name that fits hash value { static unsigned char untable[]={0,'1','2','3','4','5','6','7','8','9','0',0,'.','_',0,0,0,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}; unsigned int i,n,f,t; long double x; static char buffer[15]; for(i=0;i<hashrecs;i++) if(hashrec[i].hash==hash) return hashrec[i].name; for(t=0;t<65535U;t++) { x=hash+256.0*256.0*256.0*256.0*t; buffer[i=14]='\0'; while(1) { n=(int)fmodl(x,43.0); f=untable[n]; if(!f) break; x-=n; buffer[--i]=f; x/=43.0; if(x<1.0) return buffer+i; } } // should never happen error("Can not find a matching string for hash value %08lx\n",hash); sprintf(buffer,"HASH%08lx",hash); return buffer; } size_t myFRead(void *ptr,long bytes,FILE *f) // save fread function { size_t result; if(bytes==0) return 0; if(bytes<1||bytes!=(size_t)bytes||(result=fread(ptr,1,(size_t)bytes,f))!=bytes) { error("myFRead(%ld) at %ld failed\n",bytes,ftell(f)); } return result; } size_t StringRead(char *ptr,size_t size,FILE *f) // read nul terminated string { size_t i; int c; i=0; while((c=getc(f))>0) { if(i>=size-1) { fprintf(stderr,"\nKeyword length exceeds decompiler limit.\n"); exit(1); } ptr[i++]=c; } ptr[i]='\0'; return i; } void myFClose(FILE *f) // checks if disk is full { if(ferror(f)!=0) { fprintf(stderr,"\nDisk full. Program aborted.\n"); exit(2); } fclose(f); } FILE *myFOpen(const char *filename,const char *mode) // save fopen function { FILE *f; char ch; if(!overwrite) { f=fopen(filename,"rb"); if(f) { fclose(f); fprintf(stderr,"File %s already exists. Overwrite (Y/N/All) ? Y\b",filename); do { ch=getch(); } while(ch!='a'&&ch!='A'&&ch!='y'&&ch!='Y'&&ch!='n'&&ch!='N'&&ch!='\r'&&ch!='\x1B'); if(ch=='\r') ch='Y'; if(ch=='\x1B') ch='N'; printf("%c\n",ch); if(ch=='A'||ch=='a') { ch='Y'; overwrite=TRUE; } if(ch=='n'||ch=='N') exit(0); } } f=fopen(filename,mode); if(!f) { error("Can not create '%s'.\n",filename); } else if(((MFILE *)f)->magic==MAGIC) { fprintf(stderr,"Error creating %s...\n",filename); fclose(f); f=NULL; } else { fprintf(stderr,"Creating %s...\n",filename); } return f; } char *WindowName(long n) // secondary window name from secondary window number { int i; if(n==255||secondarywindownames==NULL) return "main"; for(i=0;secondarywindownames[i];i++) { if(i==n) return secondarywindownames[i]; } error("Secondary window %ld not defined",n); return "main"; } void AddTopic(char *TopicName) // adds a known topic name to hash decode list { long x; int i; x=hash(TopicName); for(i=0;i<hashrecs;i++) { if(hashrec[i].hash==x) { if(stricmp(TopicName,hashrec[i].name)!=0) { fprintf(stderr,"Context %s already defined as %s\n",TopicName,hashrec[i].name); } return; } } hashrec=myReAlloc(hashrec,++hashrecs*sizeof(HASHREC)); hashrec[i].name=myStrDup(TopicName); hashrec[i].hash=x; } // Why does HELPDECO try to isolate topic names from macro parameters ? // Shure, HELPDECO can generate a topic name that has the same hash code // as in WinHelp. So a topic jump into an external file will not fail. // But if a macro or hotspot uses the original topic name and HELPDECO // invents a different name delivering the same hash code, the help // compiler will emit annoying warnings. That's why we collect all // original names that we can find and generate topic names only if // no original name has been found. #if 0 void AddMacro(char *ptr) { int i,n; char buffer[128]; char *param[10]; while(1) { while(*ptr==' ') ptr++; for(i=0;i<sizeof(buffer)&&*ptr!=':'&&*ptr!='\0'&&*ptr!=';'&&*ptr!='(';i++) buffer[i]=*ptr++; while(i>0&&buffer[i-1]==' ') i--; if(*ptr=='(') { buffer[i++]=ptr++; for(n=0;n<sizeof(param)/sizeof(param[0]);n++) { while(*ptr==' ') ptr++; if(*ptr==')') break; if(*ptr=='\"') { param[n]=++ptr; while(*ptr!='\0'&&*ptr!='\"') ptr++; if(*ptr=='\0') return; *ptr++='\0'; } else if(*ptr=='`') { param[n]=++ptr; while(*ptr!='\0'&&*ptr!='\'') ptr++; if(*ptr=='\0') return; *ptr++='\0'; } else { param[n]=ptr; while(*ptr!='\0'&&*ptr!=','&&*ptr!=')') ptr++; if(*ptr=='\0') return; } while(*ptr==' ') ptr++; if(*ptr==')') break; if(*ptr!=',') return; *ptr++='\0'; } *ptr++='\0'; while(*ptr==' ') ptr++; } if(*ptr!=':'&&*ptr!=':') break; ptr++; } } #else char *AddMacro(char *ptr) { // table listing known macro names and type of parameters // m specifies a parameter that can be a macro oder marker-name // c specifies a parameter that has to be a context ID // f specifies a parameter that has to be a "filename>window" // s specifies a parameter that can be ignored char *macro[]={"AddAccelerator(ssm","AA(ssm","AppendItem(sssm","AI(sssm","ChangeButtonBinding(sm","CB(ssm","CBB(sm","CE(sm","ChangeItemBinding(sm","CIB(sm","ExtInsertItem(sssm","IfThen(mm","IT(mm","IfThenElse(mmm","IE(mmm","InsertItem(sssms","II(sssms","JumpId(fc","JI(fc","MPrintID(c","Not(m","PopupId(fc","PI(fc","UpdateWindow(fc","UW(fc"}; char *name; char *param; int i,n; while(1) { while(*ptr==' ') ptr++; name=ptr; for(n=0;isalnum((unsigned char)*ptr);n++) ptr++; while(*ptr==' ') ptr++; if(*ptr=='(') { ptr++; for(i=0;i<sizeof(macro)/sizeof(macro[0]);i++) { if(n==strcspn(macro[i],"(")&&memicmp(macro[i],name,n)==0) break; } if(i<sizeof(macro)/sizeof(macro[0])) { name=macro[i]+n+1; } else { name=NULL; } while(1) { while(*ptr==' ') ptr++; if(*ptr=='"'||*ptr=='`') { if(*ptr=='\\') { ptr+=2; } else { ptr++; } param=ptr; if(name&&*name=='m') { ptr=AddMacro(ptr); while(*ptr==' ') ptr++; } else { while(*ptr!='\0'&&*ptr!='"'&&*ptr!='\'') ptr++; } if(*ptr!='"'&&*ptr!='\'') break; *ptr='\0'; if(name&&*name=='c') AddTopic(param); ptr++; } else { while(isalnum((unsigned char)*ptr)) ptr++; } while(*ptr==' ') ptr++; if(*ptr!=',') break; ptr++; if(name) name++; } while(*ptr==' ') ptr++; if(*ptr!=')') break; ptr++; } while(*ptr==' ') ptr++; if(*ptr!=';'&&*ptr!=':') break; ptr++; } return ptr; } #endif BOOL SearchFile(FILE *HelpFile,char *FileName,long *FileOffset) // locate an internal file { FILEHEADER FileHdr; BTREEHEADER WHIFSHeader; BTREENODEHEADER CurrNode; long offset; char TempFile[19]; int i,n; fseek(HelpFile,HelpHeader.DirectoryStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); myFRead(&WHIFSHeader,sizeof(WHIFSHeader),HelpFile); offset=ftell(HelpFile); fseek(HelpFile,offset+WHIFSHeader.RootPage*(long)WHIFSHeader.PageSize,SEEK_SET); for(n=1;n<WHIFSHeader.NLevels;n++) { myFRead(&CurrNode,sizeof(BTREEINDEXHEADER),HelpFile); for(i=0;i<CurrNode.NEntries;i++) { StringRead(TempFile,sizeof(TempFile),HelpFile); if(strcmp(FileName,TempFile)<0) break; myFRead(&CurrNode.PreviousPage,sizeof(CurrNode.PreviousPage),HelpFile); } fseek(HelpFile,offset+CurrNode.PreviousPage*(long)WHIFSHeader.PageSize,SEEK_SET); } myFRead(&CurrNode,sizeof(CurrNode),HelpFile); for(i=0;i<CurrNode.NEntries;i++) { StringRead(TempFile,sizeof(TempFile),HelpFile); myFRead(FileOffset,sizeof(long),HelpFile); if(strcmp(TempFile,FileName)==0) return TRUE; } return FALSE; } short GetFirstPage(FILE *HelpFile,long FileStart,BUFFER *buffer) // walk Btree { int CurrLevel; FILEHEADER FileHdr; BTREEHEADER BTreeHdr; BTREENODEHEADER CurrNode; fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); myFRead(&BTreeHdr,sizeof(BTreeHdr),HelpFile); if(!BTreeHdr.TotalBtreeEntries) return 0; buffer->FirstLeaf=ftell(HelpFile); buffer->PageSize=BTreeHdr.PageSize; fseek(HelpFile,buffer->FirstLeaf+BTreeHdr.RootPage*(long)BTreeHdr.PageSize,SEEK_SET); for(CurrLevel=1;CurrLevel<BTreeHdr.NLevels;CurrLevel++) { myFRead(&CurrNode,sizeof(BTREEINDEXHEADER),HelpFile); fseek(HelpFile,buffer->FirstLeaf+CurrNode.PreviousPage*(long)BTreeHdr.PageSize,SEEK_SET); } myFRead(&CurrNode,sizeof(CurrNode),HelpFile); buffer->NextPage=CurrNode.NextPage; return CurrNode.NEntries; } short GetNextPage(FILE *HelpFile,BUFFER *buffer) // walk Btree { BTREENODEHEADER CurrNode; if(buffer->NextPage==-1) return 0; fseek(HelpFile,buffer->FirstLeaf+buffer->NextPage*(long)buffer->PageSize,SEEK_SET); myFRead(&CurrNode,sizeof(CurrNode),HelpFile); buffer->NextPage=CurrNode.NextPage; return CurrNode.NEntries; } void SysLoad(FILE *HelpFile) // gets global values from SYSTEM file { FILEHEADER FileHdr; long CurrentLocation; SYSTEMREC SystemRec; long FileOffset; char *data; if(SearchFile(HelpFile,"|SYSTEM",&FileOffset)) { fseek(HelpFile,FileOffset,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); myFRead(&SysHeader,sizeof(SysHeader),HelpFile); before31=SysHeader.Version<3||SysHeader.Version==3&&SysHeader.Revision<16; lzcompressed=!before31&&(SysHeader.Flags==4||SysHeader.Flags==8); if(before31) { DecompressSize=TopicBlockSize=2048; } else if(SysHeader.Flags==8) { TopicBlockSize=2048; DecompressSize=0x4000; } else { TopicBlockSize=4096; DecompressSize=0x4000; } if(before31) { fgets(HelpFileTitle,33,HelpFile); } else // else get 3.1 System records { CurrentLocation=12; while(CurrentLocation<FileHdr.FileSize) { myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile); CurrentLocation+=4; if(SystemRec.DataSize) { data=myMalloc(SystemRec.DataSize+1); myFRead(data,SystemRec.DataSize,HelpFile); data[SystemRec.DataSize]='\0'; CurrentLocation+=SystemRec.DataSize; switch(SystemRec.RecordType) { case 0x0001: if(*data) strcpy(HelpFileTitle,data); break; } free(data); } } } } } /********************************************************************************* GRAPHICS STUFF ============== ExtractBitmap(..) extracts the graphics stored as |bmXXX in the Windows HelpFile to separate files. In this version multiple resolution pictures (with and without hotspots) are saved as MRB's, pictures with hotspots are converted to the SHG-format, single resolution Bitmaps without hotspots to standard BMP-format and all Metafiles without hotspots to the Aldus Placeable Metafile WMF format. GENERAL NOTES ABOUT |bmXXX -------------------------- |bmXXX contains one MRB-File. This can be directly recompiled. If hotspots in the graphics contained in the MRB-File shall be added or changed with SHED, the MRB-File has to be split into one SHG file for each graphic. This is very easy as the graphics-data will only be slightly rewrapped. If the graphics themselves shall be edited with a standard drawing program, the SHG files have to be converted into bitmaps or metafiles (as flagged in the SHG). The hotspot data is lost by this step. MRB-FILES --------- MRBC takes the input-file(s), converts them to "lp"-Files if needed, adds a resolution specification if none is given in the file and finally outputs a "lp"-File with the ending ".MRB". Depending on the given display type MRBC sets the following resolutions: | | | x-resolution | y-resolution display-type | extension | std-extension | (ppi) | (ppi) -------------+-----------+---------------+--------------+------------- CGA | ".C*" | ".CGA" | 96 | 48 EGA | ".E*" | ".EGA" | 96 | 72 VGA | ".V*" | ".VGA" | 96 | 96 8514 | ".8*" | ".854" | 120 | 120 SHG-Files --------- Structure of SHG-Data: Offset | Size | Ref | Description ----------+------------+-----+----------------------------------------------- 0 | 1 Byte | | always 0x01, maybe a version-number 1 | 1 Word | N | the number of hotspots 3 | 1 DWord | M | size of macro-data (in bytes) 7 | 15xN Bytes | HSD | Hotspot-Data (see below) 7 + 15*N | M Bytes | | Macro-Data (ASCIIZ-Strings) 7+15*N+M | 2*N*ASCIIZ | | Hotspot-Id and Context- or Macro-Name as | | | ASCIIZ-String Structure of Hotspot-Data: Offset | Size | Ref | Description ----------+------------+-----+------------------------------------------------- 0 | 3 Bytes | | Hotspot-Type: 0xE3 0x00 0x00 = jump, visible | | | 0xE7 0x04 0x00 = jump, invisible | | | 0xE2 0x00 0x00 = pop-up, visible | | | 0xE6 0x04 0x00 = pop-up, invisible | | | 0xC8 0x00 0x00 = macro, visible | | | 0xCC 0x04 0x00 = macro, invisible 3 | 1 Word | | Left border of hotspot 5 | 1 Word | | Top border of hotspot 7 | 1 Word | | Right border - Left border 9 | 1 Word | | Bottom border - Top border 11 | 1 DWord | | 0x00000000 => nothing | | | 0x00000001 => this is a macro-hotspot | | | others => hash-value of the context-string | | | according to the WinHelp standard 03-06-1995 Holger Haase **********************************************************************************/ MFILE *CreateMap(char *buffer,unsigned int size) // assign a memory mapped file { MFILE *f; f=myMalloc(sizeof(MFILE)); if(!f) return NULL; f->magic=MAGIC; f->ptr=buffer; f->end=buffer+size; return f; } char *CloseMap(MFILE *f) // close a memory mapped file (never used) { char *ptr; if(!f) return NULL; ptr=f->ptr; free(f); return ptr; } int GetChar(MFILE *f) // getc from memory mapped file or regular file { if(f->magic!=MAGIC) return getc((FILE *)f); if(f->ptr>=f->end) return -1; return *f->ptr++; } int GetWord(MFILE *f) // getw from memory mapped file or regular file { if(f->magic!=MAGIC) return getw((FILE *)f); if(f->ptr>=f->end) return -1; return *((int *)f->ptr)++; } unsigned short GetCWord(MFILE *f) // get compressed word { unsigned char b; b = GetChar(f); if (b & 1) return (((unsigned short)GetChar(f) << 8) | (unsigned short)b) >> 1; return ((unsigned short)b >> 1); } unsigned long GetCDWord(MFILE *f) // get compressed long { unsigned short w; w = GetWord(f); if (w & 1) return (((unsigned long)GetWord(f) << 16) | (unsigned long)w) >> 1; return ((unsigned long)w >> 1); } unsigned long GetDWord(MFILE *f) // get long { unsigned short w; w = GetWord(f); return ((unsigned long)GetWord(f) << 16) | (unsigned long)w; } long copy(FILE *f,long bytes,FILE *out) { long length; int size; static char buffer[1024]; for(length=0;length<bytes;length+=size) { size=(int)(bytes-length>sizeof(buffer)?sizeof(buffer):bytes-length); myFRead(buffer,size,f); fwrite(buffer,size,1,out); } return length; } long CopyBytes(MFILE *f,long bytes,FILE *out) { if(f->magic!=MAGIC) return copy((FILE *)f,bytes,out); if(bytes>(size_t)(f->end-f->ptr)) bytes=(size_t)(f->end-f->ptr); fwrite(f->ptr,(size_t)bytes,1,out); f->ptr+=(size_t)bytes; return bytes; } void derun(int i,FILE *f) // expand runlen compressed data { static signed char count; static char runlen; if(!f) { count=0; runlen=i; } else if(runlen) { if(count&0x7F) { do { putc(i,f); count--; } while(count>0); } else { count=(signed char)i; } } else { putc(i,f); } } // decode bytes from file f into file out or into buffer unsigned short LzExpand(MFILE *f,long bytes,FILE *out,unsigned char *buffer) { static unsigned char lzbuffer[0x1000]; unsigned char b; // bits read from f signalling compressed/uncompressed unsigned char c; // decompressed char unsigned char m; // mask to test bit in b int l; unsigned int i; // where we are unsigned int p; // word read from f / from where we copy i=0; derun(out!=NULL&&buffer!=NULL,NULL); while(bytes>0L) // Go through until we're done { b=GetChar(f); bytes--; if(bytes<=0L) break; for(m=0x01;m;m<<=1) { if(b&m) // bit set: take doublebyte, decode and copy { p=GetWord(f); l=((p>>12)&15)+3; p=i-(p&0xFFF)-1; while(l--) { lzbuffer[i++&0xFFF]=c=lzbuffer[p++&0xFFF]; if(out) derun(c,out); else *buffer++=c; } bytes-=2; } else // bit clear: take byte and store { lzbuffer[i++&0xFFF]=c=GetChar(f); if(out) derun(c,out); else *buffer++=c; bytes--; } if(bytes<=0L) break; // stop if all bytes are consumed } } return i; } long DecodeData(MFILE *f,unsigned long dwDataSize,FILE *fTarget,unsigned long dwRawSize,unsigned char byPacked) { unsigned long dwTargetSize; long start; unsigned char count,value; start=ftell(fTarget); switch(byPacked) { case 0: // unpacked CopyBytes(f,dwDataSize,fTarget); break; case 1: // runlen while(dwDataSize>0) { count=GetChar(f); dwDataSize--; if(count&0x80) { count&=0x7F; dwDataSize-=count; CopyBytes(f,count,fTarget); } else { dwDataSize--; value=GetChar(f); while(count-->0) putc(value,fTarget); } } break; case 2: // LZ77 LzExpand(f,dwDataSize,fTarget,NULL); break; case 3: // both LzExpand(f,dwDataSize,fTarget,buffer); break; default: error("unknown packing method: %02X.\n",byPacked); return 0L; } dwTargetSize=ftell(fTarget)-start; if(dwTargetSize<dwRawSize) { error("Picture decompression produced %lu bytes instead of required %lu\n",dwTargetSize,dwRawSize); } else if(dwTargetSize>dwRawSize) { fprintf(stderr,"Picture decompression produced %ld bytes more than required.\n",dwTargetSize-dwRawSize); } return dwTargetSize; } void ExtractMacros(char *filename) { FILE *f; long dwHotspotOffset; int ch; unsigned int count,i,n,j,hotspots,k; long pos,length; unsigned char byType; HOTSPOT *hotspot; static char buffer[1024]; char *ptr; f=fopen(filename,"rb"); if(f) { if(((MFILE *)f)->magic==MAGIC) { error("Error opening '%s'\n",filename); } else { getw(f); count=getw(f); for(i=0;i<count;i++) { fseek(f,4+4*i,SEEK_SET); fread(&pos,1,sizeof(pos),f); fseek(f,pos,SEEK_SET); byType=getc(f); // type of picture: 5=DDB, 6=DIB, 8=METAFILE j=getc(f); // packing method: 0=unpacked, 1=RunLen, 2=LZ77, 3=both if(byType!=8&&byType!=6&&byType!=5) { fprintf(stderr,"Unknown FileType %d\n",byType); } else { if(byType==8) { GetCWord((MFILE *)f); // mapping mode getw(f); // width of metafile-picture getw(f); // height of metafile-picture GetCDWord((MFILE *)f); // dwDataSize } else { GetCDWord((MFILE *)f); // biXPelsPerMeter/(100/2.54) GetCDWord((MFILE *)f); // biYPelsPerMeter/(100/2.54) GetCWord((MFILE *)f); // biPlanes GetCWord((MFILE *)f); // biBitCount GetCDWord((MFILE *)f); // biWidth GetCDWord((MFILE *)f); // biHeight GetCDWord((MFILE *)f); // biClrUsed GetCDWord((MFILE *)f); // biClrImportant } GetCDWord((MFILE *)f); // dwRawSize GetCDWord((MFILE *)f); // dwHotspotSize GetDWord((MFILE *)f); // dwPictureOffset dwHotspotOffset=GetDWord((MFILE *)f); if(dwHotspotOffset) { fseek(f,pos+dwHotspotOffset,SEEK_SET); if(getc(f)!=-1) { hotspots=getw(f); fread(&length,1,sizeof(length),f); hotspot=myMalloc(hotspots*sizeof(HOTSPOT)); fread(hotspot,sizeof(HOTSPOT),hotspots,f); myFRead(buffer,length,f); for(n=0;n<hotspots;n++) if((hotspot[n].id0&0xFB)==0xC8) // skip macros { for(j=0;j<sizeof(buffer)-1&&(ch=getc(f))>0;j++) buffer[j]=ch; buffer[j]='\0'; } for(n=0;n<hotspots;n++) { for(j=0;j<sizeof(buffer)-1&&(ch=getc(f))>0;j++) buffer[j]=ch; buffer[j++]='\0'; for(k=j;k<sizeof(buffer)-1&&(ch=getc(f))>0;k++) buffer[k]=ch; buffer[k]='\0'; switch(hotspot[n].id0) { case 0xC8: // macro (never seen) case 0xCC: // macro without font change AddMacro(buffer+j); break; case 0xE0: // popup jump HC30 case 0xE1: // topic jump HC30 case 0xE2: // popup jump HC31 case 0xE3: // topic jump HC31 case 0xE6: // popup jump without font change case 0xE7: // topic jump without font change if(hash(buffer+j)!=hotspot[n].hash) { fprintf(stderr,"Wrong hash %08lx instead %08lx for '%s'\n",hotspot[n].hash,hash(buffer+j),buffer+j); } AddTopic(buffer+j); break; case 0xEA: // popup jump into external file case 0xEB: // topic jump into external file / secondary window case 0xEE: // popup jump into external file without font change case 0xEF: // topic jump into external file / secondary window without font change if(hotspot[n].id1!=0&&hotspot[n].id1!=1&&hotspot[n].id1!=4&&hotspot[n].id1!=6||hotspot[n].id2!=0) { } else { if(strchr(buffer+j,'@')==NULL) // no external file specified { ptr=strchr(buffer+j,'>'); if(ptr) *ptr='\0'; AddTopic(buffer+j); } break; } default: error("Unknown hotspot %02x %02x %02x X=%u Y=%u W=%u H=%u %08lx,%s,%s\n",hotspot[n].id0,hotspot[n].id1,hotspot[n].id2,hotspot[n].x,hotspot[n].y,hotspot[n].w,hotspot[n].h,hotspot[n].hash,buffer,buffer+j); } } free(hotspot); } else { fprintf(stderr,"No hotspots\n"); } } } } } fclose(f); } else { error("Can't extract macros from %s. Open failed.\n",filename); } } int GetPackedByte(MFILE *f) // RulLen decompression { static unsigned char count,value; if(!f) // initialize { count=value=0; } else { if((count&0x7F)==0) { count=GetChar(f); value=GetChar(f); } else if(count&0x80) { value=GetChar(f); } count--; } return value; } BOOL CheckHash(char *filename,long hash) { static char *notfound[20]; static int notfounds=0; FILE *f; long offset; int i,j,k,l,n; FILEHEADER FileHdr; BTREEHEADER BTreeHdr; CONTEXTREC ContextRec; // open file and check if it is a Windows help file f=fopen(filename,"rb"); if(!f) { for(i=0;i<notfounds;i++) if(strcmp(notfound[i],filename)==0) return FALSE; fprintf(stderr,"%s not found\n",filename); if(notfounds<sizeof(notfound)/sizeof(notfound[0])) notfound[notfounds++]=strdup(filename); return FALSE; } if(getw(f)==0x5F3F&&getw(f)==0x0003) { // locate the |CONTEXT internal file using the internal directory fread(&offset,sizeof(offset),1,f); fseek(f,offset,SEEK_SET); fread(&FileHdr,sizeof(FileHdr),1,f); fread(&BTreeHdr,sizeof(BTreeHdr),1,f); offset=ftell(f); n=BTreeHdr.RootPage; for(i=1;i<BTreeHdr.NLevels;i++) { fseek(f,offset+n*(long)BTreeHdr.PageSize,SEEK_SET); getw(f); j=getw(f); // NEntries n=getw(f); // PreviousPage for(k=0;k<j;k++) { for(l=0;(buffer[l]=getc(f))!='\0';l++) ; if(strcmp("|CONTEXT",buffer)<0) break; n=getw(f); // Page } } fseek(f,offset+n*(long)BTreeHdr.PageSize,SEEK_SET); getw(f); j=getw(f); // NEntries getw(f); // PreviosPage; getw(f); // NextPage for(i=0;i<j;i++) { for(l=0;(buffer[l]=getc(f))!='\0';l++) ; fread(&offset,sizeof(offset),1,f); if(strcmp("|CONTEXT",buffer)==0) { // found |CONTEXT in internal directory. Go to this file // and locate the specified entry fseek(f,offset,SEEK_SET); fread(&FileHdr,sizeof(FileHdr),1,f); fread(&BTreeHdr,sizeof(BTreeHdr),1,f); offset=ftell(f); n=BTreeHdr.RootPage; for(i=1;i<BTreeHdr.NLevels;i++) { fseek(f,offset+n*(long)BTreeHdr.PageSize,SEEK_SET); getw(f); j=getw(f); // NEntries n=getw(f); // PreviousPage for(k=0;k<j;k++) { fread(&ContextRec.HashValue,sizeof(ContextRec.HashValue),1,f); if(ContextRec.HashValue<=hash) break; n=getw(f); // Page } } fseek(f,offset+n*(long)BTreeHdr.PageSize,SEEK_SET); getw(f); j=getw(f); // NEntries getw(f); // PreviosPage; getw(f); // NextPage for(i=0;i<j;i++) { fread(&ContextRec,sizeof(ContextRec),1,f); if(ContextRec.HashValue==hash) { fclose(f); return TRUE; } if(ContextRec.HashValue>hash) break; } } } } else { for(i=0;i<notfounds;i++) if(strcmp(notfound[i],filename)==0) return FALSE; fprintf(stderr,"%s is no valid WinHelp file\n",filename); if(notfounds<sizeof(notfound)/sizeof(notfound[0])) notfound[notfounds++]=strdup(filename); } fclose(f); return FALSE; } void CheckMacro(char *buffer) { fprintf(stderr,"Macro: %s\n",buffer); } void CheckBitmap(MFILE *f) { char byType,byPacked; int n,j; long pos,offset,dwHotspotOffset,dwHotspotSize; pos=myFTell(f); if((GetWord(f)&0xDFFF)==0x506C) { n=GetWord(f); for(j=0;j<n;j++) { myFSeek(f,pos+4*(j+1)); offset=GetDWord(f); myFSeek(f,pos+offset); byType=GetChar(f); // type of picture: 5=DDB, 6=DIB, 8=METAFILE byPacked=GetChar(f); // packing method: 0=unpacked, 1=RunLen, 2=LZ77 dwHotspotOffset=0L; if(byType==6||byType==5&&byPacked<2) { GetCDWord(f); GetCDWord(f); GetCWord(f); GetCWord(f); GetCDWord(f); GetCDWord(f); GetCDWord(f); GetCDWord(f); GetCDWord(f); dwHotspotSize=GetCDWord(f); GetDWord(f); // dwPictureOffset dwHotspotOffset=GetDWord(f); } else if(byType==8) // Windows MetaFile { GetCWord(f); // mapping mode GetWord(f); // width of metafile-picture GetWord(f); // height of metafile-picture GetCDWord(f); GetCDWord(f); dwHotspotSize=GetCDWord(f); // dwHotspotSize GetDWord(f); // dwPictureOffset dwHotspotOffset=GetDWord(f); } if(dwHotspotOffset) // dwHotspotOffset { myFSeek(f,offset+dwHotspotOffset); if(GetChar(f)==1) { int i,l,n; HOTSPOT *hotspot; n=GetWord(f); if(n>0) { hotspot=malloc(n*sizeof(HOTSPOT)); if(hotspot) { GetWord(f); GetWord(f); for(i=0;i<n*sizeof(HOTSPOT);i++) ((char *)hotspot)[i]=GetChar(f); for(i=0;i<n;i++) if((hotspot[i].id0&0xFB)==0xC8) // skip macros { for(l=0;l<sizeof(buffer)&&(buffer[l]=GetChar(f))!='\0';l++); // skip macro } for(i=0;i<n;i++) { char *filename; char *ptr; for(l=0;l<sizeof(buffer)&&(buffer[l]=GetChar(f))!='\0';l++); // skip name for(l=0;l<sizeof(buffer)&&(buffer[l]=GetChar(f))!='\0';l++); switch(hotspot[i].id0) { case 0xC8: // macro (never seen) case 0xCC: // macro without font change CheckMacro(buffer); break; case 0xEA: case 0xEB: case 0xEE: case 0xEF: filename=strchr(buffer,'@'); if(filename) // external file { *filename++='\0'; ptr=strchr(buffer,'>'); if(ptr) *ptr='\0'; if(!CheckHash(filename,hash(buffer))) { fprintf(stderr,"%s@%s not satisfied\n",buffer,filename); } } } } free(hotspot); } } } } } } } int ExtractBitmap(char *szFilename,MFILE *f,long FileSize) { unsigned char byType; unsigned char byPacked; long l; BITMAPFILEHEADER bmfh; BITMAPINFOHEADER bmih; int colors; long FileStart; unsigned short wMagic; unsigned long dwRawSize,dwDataSize,dwHotspotOffset,dwOffsBitmap; unsigned int i; FILE *fTarget; int type; FileStart=myFTell(f); wMagic=GetWord(f); if((wMagic&0xDFFF)!=0x506C) { fprintf(stderr,"Unknown bitmap wMagic %04X (%c%c)\n",wMagic,wMagic&0x00FF,wMagic>>8); // output file for debugging fTarget=myFOpen(szFilename,"wb"); if(fTarget) { putw(wMagic,fTarget); CopyBytes(f,FileSize-2,fTarget); myFClose(fTarget); } return 0; } // wMagic & 0x2000 unknown if(GetWord(f)>1) // number of bitmaps (MultiResolutionBitmap) { strcat(szFilename,".MRB"); type=3; } else { dwOffsBitmap=GetDWord(f); myFSeek(f,FileStart+dwOffsBitmap); byType=GetChar(f); // type of picture: 5=DDB, 6=DIB, 8=METAFILE byPacked=GetChar(f); // packing method: 0=unpacked, 1=RunLen, 2=LZ77 if(byType==6||byType==5&&byPacked<2) { memset(&bmfh,0,sizeof(bmfh)); memset(&bmih,0,sizeof(bmih)); bmfh.bfType=0x4D42; // bitmap magic ("BM") bmih.biSize=sizeof(bmih); // HC30 doesn't like certain PelsPerMeter, so let them be 0 /* bmih.biXPelsPerMeter = 40L* */ GetCDWord(f); /* bmih.biYPelsPerMeter = 40L* */ GetCDWord(f); bmih.biPlanes=GetCWord(f); bmih.biBitCount=GetCWord(f); bmih.biWidth=GetCDWord(f); bmih.biHeight=GetCDWord(f); colors=(int)(bmih.biClrUsed=GetCDWord(f)); if(!colors) colors=1<<bmih.biBitCount; bmih.biClrImportant=GetCDWord(f); dwDataSize=GetCDWord(f); GetCDWord(f); // dwHotspotSize GetDWord(f); // dwPictureOffset dwHotspotOffset=GetDWord(f); if(!dwHotspotOffset) { strcat(szFilename,".BMP"); fTarget=myFOpen(szFilename,"wb"); if(fTarget) { fwrite(&bmfh,1,sizeof(bmfh),fTarget); fwrite(&bmih,1,sizeof(bmih),fTarget); if(byType==6) { CopyBytes(f,colors*4L,fTarget); } else { l=0x000000L; fwrite(&l,1,sizeof(l),fTarget); l=0xFFFFFFL; fwrite(&l,1,sizeof(l),fTarget); } bmfh.bfOffBits=sizeof(bmfh)+sizeof(bmih)+colors*4L; bmih.biSizeImage=(((bmih.biWidth*bmih.biBitCount+31)/32)*4)*bmih.biHeight; if(byType==5) // convert 3.0 DDB to 3.1 DIB { long width,length; int pad; width=((bmih.biWidth*bmih.biBitCount+15)/16)*2; pad=(int)(((width+3)/4)*4-width); GetPackedByte(NULL); for(l=0;l<bmih.biHeight;l++) { if(byPacked==1) { for(length=0;length<width;length++) putc(GetPackedByte(f),fTarget); } else { CopyBytes(f,width,fTarget); } if(pad) fwrite(buffer,pad,1,fTarget); } } else { DecodeData(f,dwDataSize,fTarget,bmih.biSizeImage,byPacked); } // update bitmap headers bmfh.bfSize=ftell(fTarget); fseek(fTarget,0L,SEEK_SET); fwrite(&bmfh,1,sizeof(bmfh),fTarget); fwrite(&bmih,1,sizeof(bmih),fTarget); myFClose(fTarget); } if(bmih.biPlanes==1&&bmih.biBitCount==4&&bmih.biClrImportant==1) return 5; return 1; } strcat(szFilename,".SHG"); type=2; } else if(byType==8) // Windows MetaFile { APMFILEHEADER afh; unsigned short *wp; memset(&afh,0,sizeof(afh)); afh.dwKey=0x9AC6CDD7L; GetCWord(f); // mapping mode afh.rcBBox.right=GetWord(f); // width of metafile-picture afh.rcBBox.bottom=GetWord(f); // height of metafile-picture dwRawSize=GetCDWord(f); dwDataSize=GetCDWord(f); GetCDWord(f); // dwHotspotSize GetDWord(f); // dwPictureOffset dwHotspotOffset=GetDWord(f); if(!dwHotspotOffset) { afh.wInch=2540; wp=(unsigned short *)&afh; for(i=0;i<10;i++) afh.wChecksum^=*wp++; strcat(szFilename,".WMF"); fTarget=myFOpen(szFilename,"wb"); if(fTarget) { fwrite(&afh,1,sizeof(afh),fTarget); DecodeData(f,dwDataSize,fTarget,dwRawSize,byPacked); myFClose(fTarget); } return 4; } strcat(szFilename,".SHG"); type=6; } else { fprintf(stderr,"Unknown byType %02x byPacked=%02x. %s skipped.\n",byType,byPacked,szFilename); return 0; } } myFSeek(f,FileStart); fTarget=myFOpen(szFilename,"wb"); if(fTarget) { CopyBytes(f,FileSize,fTarget); myFClose(fTarget); if(extractmacros) ExtractMacros(szFilename); } return type; // 2 = SHG, 3 = MRB, 6 = SHG Metafile } //*************************************************************************** // END OF GRAPHICS STUFF //*************************************************************************** void ListFiles(FILE *HelpFile) { BUFFER buf; char FileName[20]; long FileOffset; int n,i,entries; printf("FileName FileOffset | FileName FileOffset\n"); printf("-----------------------------------+-----------------------------------\n"); entries=GetFirstPage(HelpFile,HelpHeader.DirectoryStart,&buf); n=0; while(entries) { for(i=0;i<entries;i++) { StringRead(FileName,sizeof(FileName),HelpFile); myFRead(&FileOffset,sizeof(FileOffset),HelpFile); printf("%-23s 0x%08lX %s",FileName,FileOffset,(n%2?"\n":"| ")); n++; } entries=GetNextPage(HelpFile,&buf); } if(n%2) printf("\n"); } char *getbitmapname(unsigned int n) { static char name[12]; if(n<extensions&&extension[n]) { sprintf(name,"bm%u.%s",n,bmpext[extension[n]&0x0F]); } else if(n==65535U) { missing++; fprintf(stderr,"\nThere was a picture file missing on creation of helpfile.\n"); strcpy(name,"missing.bmp"); } else { warnings=TRUE; if(warn) fprintf(stderr,"\nBitmap bm%u not exported\n",n); sprintf(name,"bm%u.bmp",n); } return name; } void ListBitmaps(FILE *hpj) { unsigned int n; if(hpj&&extensions) { fprintf(hpj,"[BITMAPS]\n"); for(n=0;n<extensions;n++) if(extension[n]) { fprintf(hpj,"bm%u.%s\n",n,bmpext[extension[n]&0x0F]); } fprintf(hpj,"\n"); } } void ExportBitmaps(FILE *HelpFile) { BUFFER buf; FILEHEADER FileHdr; char *leader; char FileName[20]; long FileOffset; int i,j,entries,type; long savepos; leader="|bm"+before31; entries=GetFirstPage(HelpFile,HelpHeader.DirectoryStart,&buf); while(entries) { for(i=0;i<entries;i++) { StringRead(FileName,sizeof(FileName),HelpFile); myFRead(&FileOffset,sizeof(FileOffset),HelpFile); if(memcmp(FileName,leader,strlen(leader))==0) { savepos=ftell(HelpFile); fseek(HelpFile,FileOffset,SEEK_SET); fread(&FileHdr,sizeof(FileHdr),1,HelpFile); if(checkexternal) { CheckBitmap((MFILE *)HelpFile); } else { type=ExtractBitmap(FileName+(FileName[0]=='|'),(MFILE *)HelpFile,FileHdr.FileSize); if(type) { j=atoi(FileName+(FileName[0]=='|')+2); if(j>=extensions) { extension=realloc(extension,(j+1)*sizeof(char)); while(extensions<=j) extension[extensions++]=0; } extension[j]=type; } } fseek(HelpFile,savepos,SEEK_SET); } } entries=GetNextPage(HelpFile,&buf); } } void ListBaggage(FILE *HelpFile,FILE *hpj) { BOOL headerwritten; char *leader; char FileName[20]; long FileOffset; BUFFER buf; int i,entries; FILEHEADER FileHdr; FILE *f; long savepos; if(hpj) { headerwritten=FALSE; leader="|bm"+before31; entries=GetFirstPage(HelpFile,HelpHeader.DirectoryStart,&buf); while(entries) { for(i=0;i<entries;i++) { StringRead(FileName,sizeof(FileName),HelpFile); myFRead(&FileOffset,sizeof(FileOffset),HelpFile); if(FileName[0]!='|'&&memcmp(FileName,leader,strlen(leader))!=0&&!strstr(FileName,".GRP")) { if(!headerwritten) { fprintf(hpj,"[BAGGAGE]\n"); headerwritten=TRUE; } savepos=ftell(HelpFile); fseek(HelpFile,FileOffset,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); fprintf(hpj,"%s\n",FileName); f=myFOpen(FileName,"wb"); if(f) { copy(HelpFile,FileHdr.FileSize,f); myFClose(f); } fseek(HelpFile,savepos,SEEK_SET); } } entries=GetNextPage(HelpFile,&buf); } if(headerwritten) fprintf(hpj,"\n"); } } void HexDump(FILE *HelpFile,long FileStart) { FILEHEADER FileHdr; char Buffer[16]; long i; int BytesToPrint,Index; fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); printf("File Size: 0x%08lX\n\n",FileHdr.FileSize); printf("Offset Hex Values Ascii\n"); printf("-------------------------------------------------------------------------\n"); for(i=0;i<FileHdr.FileSize;i+=16) { printf("0x%08lX: ",i); BytesToPrint=(int)(FileHdr.FileSize-i>16?16:FileHdr.FileSize-i); myFRead(Buffer,BytesToPrint,HelpFile); for(Index=0;Index<BytesToPrint;Index++) printf("%02X ",(unsigned char) Buffer[Index]); for(Index=0;Index<16-BytesToPrint;Index++) printf(" "); for(Index=0;Index<BytesToPrint;Index++) { putchar(isprint((unsigned char)Buffer[Index])?Buffer[Index]:'.'); } putchar('\n'); } } void HexDumpMemory(unsigned char *bypMem,long lAnz) { long i; int BytesToPrint,Index; printf("\nOffset Hex Values Ascii\n"); printf("-------------------------------------------------------------------------\n"); for(i=0;i<lAnz;i+=16) { printf("0x%08lX: ",i); BytesToPrint=(int)(lAnz-i>16?16:lAnz-i); for(Index=0;Index<BytesToPrint;Index++) printf("%02X ",(unsigned char)bypMem[Index]); for(Index=0;Index<16-BytesToPrint;Index++) printf(" "); for(Index=0;Index<BytesToPrint;Index++) putchar(isprint((unsigned char)bypMem[Index])?bypMem[Index]:'.'); putchar('\n'); bypMem+=16; } putchar('\n'); } void PhrImageDump(FILE *HelpFile,long FileStart) { FILEHEADER FileHdr; PHRINDEXHDR PhrIndexHdr; unsigned short bytes; unsigned char *buffer; if(SearchFile(HelpFile,"|PhrIndex",&FileStart)) { fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); myFRead(&PhrIndexHdr,sizeof(PhrIndexHdr),HelpFile); if(SearchFile(HelpFile,"|PhrImage",&FileStart)) { if(PhrIndexHdr.phrimagesize==PhrIndexHdr.phrimagecompressedsize) { HexDump(HelpFile,FileStart); } else { fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); if(FileHdr.FileSize!=PhrIndexHdr.phrimagecompressedsize) { fprintf(stderr,"PhrImage FileSize %ld, in PhrIndex.FileHdr %ld\n",PhrIndexHdr.phrimagecompressedsize,FileHdr.FileSize); } buffer=myMalloc(PhrIndexHdr.phrimagesize); bytes=LzExpand((MFILE *)HelpFile,FileHdr.FileSize,NULL,buffer); if(bytes!=PhrIndexHdr.phrimagesize) { fprintf(stderr,"PhrImage Size %ld, in PhrIndex %ld\n",PhrIndexHdr.phrimagesize,FileHdr.FileSize); } HexDumpMemory(buffer,bytes); free(buffer); } } } } char *TopicName(long topic,BOOL removeduplicates) { static char buffer[20]; int i; if(before31) { if(topic==0L) topic=Topic[0]; for(i=16;i<Topics;i++) if(Topic[i]==topic) { sprintf(buffer,"TOPIC%d",i); return buffer; } } else { if(topic==-1L) { NotInAnyTopic=TRUE; return "21KSYK4"; // evaluates to -1 without generating help compiler warning } for(i=0;i<ContextRecs;i++) { if((ContextRec[i].TopicOffset&0x7FFFFFFFL)==topic) { if(removeduplicates) { while(i+1<ContextRecs&&ContextRec[i].TopicOffset==(ContextRec[i+1].TopicOffset|0x80000000L)) { i++; } if(ContextRec[i].TopicOffset&0x80000000L) fprintf(stderr,"Help Compiler will issue warning 3036: The map string \"%s\" has already been used.\n",unhash(ContextRec[i].HashValue)); ContextRec[i].TopicOffset|=0x80000000L; } return unhash(ContextRec[i].HashValue); } } } fprintf(stderr,"Can not find topic offset %08lx\n",topic); return NULL; } void SysDump(FILE *HelpFile,long FileStart) { FILEHEADER FileHdr; SYSTEMREC SystemRec; unsigned short CurrentLocation; struct tm *TimeRec; SECWINDOW *SWin; // Secondary Window record char *data; char *ptr; fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); myFRead(&SysHeader,sizeof(SysHeader),HelpFile); printf("|SYSTEM Dump\n\n"); // Figure out Version and Revision if(SysHeader.Version==3&&SysHeader.Revision==15) printf("HC.EXE 3.00 Help Compiler used.\n"); else if(SysHeader.Version==3&&SysHeader.Revision==21) printf("HC.EXE 3.10 Help Compiler used.\n"); else if(SysHeader.Version==3&&SysHeader.Revision==27) printf("WMVC.EXE Multimedia Compiler used.\n"); else if(SysHeader.Version==3&&SysHeader.Revision==33) { if(mvp) printf("MVC.EXE Multimedia Compiler used.\n"); else printf("HCW.EXE 4.00 Windows 95 Help Compiler used.\n"); } else printf("Unknown Compiler Version %d Revision %02d used!\n",SysHeader.Version,SysHeader.Revision); printf("System Flags & Compression Method=0x%04x\n",SysHeader.Flags); if(SysHeader.Always1!=1) fprintf(stderr,"SysHeader.Always1=%04x\n",SysHeader.Always1); if(SysHeader.Always0!=0) fprintf(stderr,"SysHeader.Always0=%04x\n",SysHeader.Always0); TimeRec=localtime(&SysHeader.GenDate); printf("Help File Generated: %s",asctime(TimeRec)); CurrentLocation=12; if(SysHeader.Version<3||SysHeader.Version==3&&SysHeader.Revision<0x10) { fgets(HelpFileTitle,33,HelpFile); printf("TITLE=%s\n",HelpFileTitle); } else while(CurrentLocation<FileHdr.FileSize) { myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile); data=myMalloc(SystemRec.DataSize+6); myFRead(data,SystemRec.DataSize,HelpFile); data[SystemRec.DataSize]='\0'; CurrentLocation=CurrentLocation+4+SystemRec.DataSize; switch(SystemRec.RecordType) { case 0x0001: printf("TITLE=%s\n",data); break; case 0x0002: ptr=strchr(data,'\r'); if(ptr) strcpy(ptr,"%date"); printf("COPYRIGHT=%s\n",data); break; case 0x0003: printf("CONTENTS=0x%08lX\n",*(long *)data); break; case 0x0004: printf("[MACRO] %s\n",data); break; case 0x0005: printf("Icon in System record\n"); break; case 0x0006: SWin=(SECWINDOW *)data; if(SWin->Flags&WSYSFLAG_TYPE) printf("Type: %s\n",SWin->Type); if(SWin->Flags&WSYSFLAG_NAME) printf("Name: %s\n",SWin->Name); if(SWin->Flags&WSYSFLAG_CAPTION) printf("Caption: %s\n",SWin->Caption); if(SWin->Flags&WSYSFLAG_X) printf("X: %d\n",SWin->X); if(SWin->Flags&WSYSFLAG_Y) printf("Y: %d\n",SWin->Y); if(SWin->Flags&WSYSFLAG_WIDTH) printf("Width: %d\n",SWin->Width); if(SWin->Flags&WSYSFLAG_HEIGHT) printf("Height: %d\n",SWin->Height); if(SWin->Maximize) printf("State&Buttons: 0x%04x\n",SWin->Maximize); if(SWin->Flags&WSYSFLAG_RGB) printf("RGB Foreground Colors Set\n"); if(SWin->Flags&WSYSFLAG_RGBNSR) printf("RGB For Non-Scrollable Region Set\n"); if(SWin->Flags&WSYSFLAG_TOP) printf("Always On Top\n"); if(SWin->Flags&WSYSFLAG_AUTOSIZEHEIGHT) printf("Auto-Size Height\n"); if(SWin->Flags&0xF000) printf("Additional Flags: 0x%04x\n",SWin->Flags); break; case 0x0008: printf("CITATION=%s\n",data); break; case 0x0009: if(!mvp) printf("LCID=0x%X 0x%X 0x%X\n",*(short *)(data+8),*(short *)data,*(short *)(data+2)); break; case 0x000A: if(!mvp) printf("CNT=%s\n",data); break; case 0x000B: if(!mvp) printf("CHARSET=%d\n",*(unsigned char *)(data+1)); break; case 0x000C: if(mvp) { printf("[FTINDEX] dtype %s\n",data); } else { printf("DEFFONT=%s,%d,%d\n",data+2,*(unsigned char *)data,*(unsigned char *)(data+1)); } break; case 0x000D: if(mvp) printf("[GROUPS] %s\n",data); break; case 0x000E: if(mvp) { printf("[KEYINDEX] keyword=%c, \"%s\"\n",data[1],data+30); } else { printf("INDEX_SEPARATORS=\"%s\"\n",data); } break; default: fprintf(stderr,"Unknown record type: 0x%04X\n",SystemRec.RecordType); HexDumpMemory(data,SystemRec.DataSize); } free(data); } } void SysList(FILE *HelpFile,FILE *hpj,char *IconFileName) { FILEHEADER FileHdr; SYSTEMREC SystemRec; SECWINDOW *SWin; unsigned short CurrentLocation; long lastpos; char HelpFileTitle[51]; long FileStart; char *data; char *ptr; int n; long color; FILE *f; int fbreak,macro,windows,i,dtype,groups,keyword; if(hpj&&SearchFile(HelpFile,"|SYSTEM",&FileStart)) { fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); myFRead(&SysHeader,sizeof(SysHeader),HelpFile); if(SysHeader.Version==3&&SysHeader.Revision==15) { strcpy(helpcomp,"HC30"); } else if(SysHeader.Version==3&&SysHeader.Revision==21) { strcpy(helpcomp,"HC31"); } else if(SysHeader.Version==3&&SysHeader.Revision==27) { strcpy(helpcomp,"WMVC"); } else if(SysHeader.Version==3&&SysHeader.Revision==33) { strcpy(helpcomp,mvp?"MVC":"HCW"); } fprintf(hpj,"[OPTIONS]\n"); if(before31) // If 3.0 get title { fgets(HelpFileTitle,33,HelpFile); if(HelpFileTitle[0]!='\0'&&HelpFileTitle[0]!='\n') { fprintf(hpj,"TITLE=%s\n",HelpFileTitle); fprintf(hpj,"INDEX=%s\n",TopicName(0L,FALSE)); } if(PhraseCount) { fprintf(hpj,"COMPRESS=TRUE\n"); } else { fprintf(hpj,"COMPRESS=FALSE\n"); } lists['K'-'A']=SearchFile(HelpFile,"|KWDATA",&FileStart)&&SearchFile(HelpFile,"|KWBTREE",&FileStart); fprintf(hpj,"\n"); } else // else get 3.1 System records { CurrentLocation=12; lastpos=ftell(HelpFile); SWin=NULL; macro=0; fbreak=0; windows=0; groups=0; keyword=0; while(CurrentLocation<FileHdr.FileSize) { myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile); CurrentLocation+=4; if(SystemRec.DataSize) { data=myMalloc(SystemRec.DataSize+6); myFRead(data,SystemRec.DataSize,HelpFile); data[SystemRec.DataSize]='\0'; CurrentLocation+=SystemRec.DataSize; switch(SystemRec.RecordType) { case 0x0001: if(*data) fprintf(hpj,"TITLE=%s\n",data); break; case 0x0002: ptr=strchr(data,'\r'); if(ptr) strcpy(ptr,"%date"); if(*data) fprintf(hpj,"COPYRIGHT=%s\n",data); break; case 0x0003: if(*(long *)data!=0L) { ptr=TopicName(*(long *)data,FALSE); if(ptr) fprintf(hpj,"CONTENTS=%s\n",ptr); } break; case 0x0004: macro=1; break; case 0x0005: fprintf(hpj,"ICON=%s\n",IconFileName); f=myFOpen(IconFileName,"wb"); if(f) { fwrite(data,SystemRec.DataSize,1,f); myFClose(f); } break; case 0x0006: windows++; break; case 0x0008: if(*data) fprintf(hpj,"CITATION=%s\n",data); break; case 0x0009: if(!mvp) fprintf(hpj,"LCID=0x%X 0x%X 0x%X\n",*(short *)(data+8),*(short *)data,*(short *)(data+2)); break; case 0x000A: if(!mvp&&*data) fprintf(hpj,"CNT=%s\n",data); break; case 0x000B: if(!mvp) fprintf(hpj,"CHARSET=%d\n",*(unsigned char *)(data+1)); break; case 0x000C: if(mvp) { fbreak=1; } else { fprintf(hpj,"DEFFONT=%s,%d,%d\n",data+2,*(unsigned char *)data,*(unsigned char *)(data+1)); } break; case 0x000D: if(mvp) groups=1; break; case 0x000E: if(mvp) { keyword=1; } else { fprintf(hpj,"INDEX_SEPARATORS=\"%s\"\n",data); strcpy(index_separators,data); } break; } free(data); } } if(SysHeader.Version>3||SysHeader.Version==3&&SysHeader.Revision>=33) { i=0; if(lzcompressed) i|=8; if(NewPhrases) i|=4; else if(PhraseCount) i|=2; fprintf(hpj,"COMPRESS=%d\n",i); } else if(!lzcompressed) { fprintf(hpj,"COMPRESS=FALSE\n"); } else if(PhraseCount) { fprintf(hpj,"COMPRESS=TRUE\n"); } else { fprintf(hpj,"COMPRESS=MEDIUM\n"); } if(SysHeader.Flags==8) fprintf(hpj,"CDROMOPT=TRUE\n"); for(i='A';i<='z';i++) { char data[10]; char tree[10]; sprintf(data,"|%cWDATA",i); sprintf(tree,"|%cWBTREE",i); lists[i-'A']=SearchFile(HelpFile,data,&FileStart)&&SearchFile(HelpFile,tree,&FileStart); if(i!='K'&&i!='A'&&i!='a'&&i!='k'&&lists[i-'A']) fprintf(hpj,"MULTIKEY=%c\n",i); } fprintf(hpj,"\n"); if(windows) { secondarywindownames=myMalloc((windows+1)*sizeof(char *)); for(i=0;i<=windows;i++) secondarywindownames[i]=NULL; fseek(HelpFile,lastpos,SEEK_SET); CurrentLocation=12; fprintf(hpj,"[WINDOWS]\n"); i=0; while(CurrentLocation<FileHdr.FileSize) { myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile); data=myMalloc(SystemRec.DataSize+1); myFRead(data,SystemRec.DataSize,HelpFile); data[SystemRec.DataSize]='\0'; CurrentLocation=CurrentLocation+4+SystemRec.DataSize; switch(SystemRec.RecordType) { case 0x0006: SWin=(SECWINDOW *)data; if(SWin->Flags&WSYSFLAG_NAME) { fprintf(hpj,"%s",SWin->Name); secondarywindownames[i]=myStrDup(SWin->Name); } i++; fprintf(hpj,"="); if(SWin->Flags&WSYSFLAG_CAPTION) fprintf(hpj,"\"%s\"",SWin->Caption); fprintf(hpj,","); if(SWin->Flags&(WSYSFLAG_X|WSYSFLAG_Y|WSYSFLAG_WIDTH|WSYSFLAG_HEIGHT)) { fprintf(hpj,"("); if(SWin->Flags&WSYSFLAG_X) fprintf(hpj,"%d",SWin->X); fprintf(hpj,","); if(SWin->Flags&WSYSFLAG_Y) fprintf(hpj,"%d",SWin->Y); fprintf(hpj,","); if(SWin->Flags&WSYSFLAG_WIDTH) fprintf(hpj,"%d",SWin->Width); fprintf(hpj,","); if(SWin->Flags&WSYSFLAG_HEIGHT) fprintf(hpj,"%d",SWin->Height); fprintf(hpj,")"); } fprintf(hpj,","); if(SWin->Maximize) fprintf(hpj,"%d",SWin->Maximize); fprintf(hpj,","); if(SWin->Flags&WSYSFLAG_RGB) fprintf(hpj,"(%d,%d,%d)",SWin->Rgb[0],SWin->Rgb[1],SWin->Rgb[2]); fprintf(hpj,","); if(SWin->Flags&WSYSFLAG_RGBNSR) fprintf(hpj,"(%d,%d,%d)",SWin->RgbNsr[0],SWin->RgbNsr[1],SWin->RgbNsr[2]); if(SWin->Flags&(WSYSFLAG_TOP|WSYSFLAG_AUTOSIZEHEIGHT)) { if(SWin->Flags&WSYSFLAG_AUTOSIZEHEIGHT) { if(SWin->Flags&WSYSFLAG_TOP) { fprintf(hpj,",f3"); } else { fprintf(hpj,",f2"); } } else fprintf(hpj,",1"); } fprintf(hpj,"\n"); break; } free(data); } fprintf(hpj,"\n"); } if(macro) { fseek(HelpFile,lastpos,SEEK_SET); CurrentLocation=12; fprintf(hpj,"[CONFIG]\n"); while(CurrentLocation<FileHdr.FileSize) { myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile); data=myMalloc(SystemRec.DataSize+1); myFRead(data,SystemRec.DataSize,HelpFile); data[SystemRec.DataSize]='\0'; CurrentLocation=CurrentLocation+4+SystemRec.DataSize; switch(SystemRec.RecordType) { case 0x0004: if(sscanf(data,"SPC(%ld)%n",&color,&n)>0) { fprintf(hpj,"SPC(%u,%u,%u)%s\n",(unsigned char)(color),(unsigned char)(color>>8),(unsigned char)(color>>16),data+n); } else { fprintf(hpj,"%s\n",data); } break; } free(data); } fprintf(hpj,"\n"); } if(fbreak) { fseek(HelpFile,lastpos,SEEK_SET); CurrentLocation=12; fprintf(hpj,"[FTINDEX]\n"); while(CurrentLocation<FileHdr.FileSize) { myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile); data=myMalloc(SystemRec.DataSize+1); myFRead(data,SystemRec.DataSize,HelpFile); data[SystemRec.DataSize]='\0'; CurrentLocation=CurrentLocation+4+SystemRec.DataSize; switch(SystemRec.RecordType) { case 0x000C: switch(sscanf(data,"%d%s%s%s%s",&dtype,buffer,buffer+256,buffer+512,buffer+1024)) { case 5: fprintf(hpj,"dtype%d=%s!%s,%s,%s\n",dtype,buffer,buffer+256,buffer+512,buffer+1024); break; case 4: fprintf(hpj,"dtype%d=%s!%s,%s\n",dtype,buffer,buffer+256,buffer+512); break; case 3: fprintf(hpj,"dtype%d=%s!%s\n",dtype,buffer,buffer+256); break; default: fprintf(stderr,"Unknown format in '%s'\n",data); } break; } free(data); } fprintf(hpj,"\n"); } if(groups) { fseek(HelpFile,lastpos,SEEK_SET); CurrentLocation=12; fprintf(hpj,"[GROUPS]\n"); while(CurrentLocation<FileHdr.FileSize) { myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile); data=myMalloc(SystemRec.DataSize+1); myFRead(data,SystemRec.DataSize,HelpFile); data[SystemRec.DataSize]='\0'; CurrentLocation=CurrentLocation+4+SystemRec.DataSize; switch(SystemRec.RecordType) { case 0x000D: groups=strcspn(data,"."); if(strcmp(data+groups,".GRP \"\" ")==0) { data[groups]='\0'; fprintf(hpj,"group=%s\n",data); } else if(memcmp(data+groups,".GRP ",5)==0) { data[groups]='\0'; fprintf(hpj,"group=%s,%s\n",data,data+groups+5); } break; } free(data); } fprintf(hpj,"\n"); } if(keyword) { fseek(HelpFile,lastpos,SEEK_SET); CurrentLocation=12; fprintf(hpj,"[KEYINDEX]\n"); while(CurrentLocation<FileHdr.FileSize) { myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile); data=myMalloc(SystemRec.DataSize+1); myFRead(data,SystemRec.DataSize,HelpFile); data[SystemRec.DataSize]='\0'; CurrentLocation=CurrentLocation+4+SystemRec.DataSize; switch(SystemRec.RecordType) { case 0x000E: fprintf(hpj,"keyword=%c,\"%s\"\n",data[1],data+30); keyindex[data[1]-'A']=TRUE; break; } free(data); } fprintf(hpj,"\n"); } for(i=0;i<windows;i++) { sprintf(HelpFileTitle,"|CF%d",i); if(SearchFile(HelpFile,HelpFileTitle,&FileStart)) { fprintf(hpj,"[CONFIG:%d]\n",i); fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); for(macro=0;macro<FileHdr.FileSize;macro+=strlen(buffer)+1) { StringRead(buffer,sizeof(buffer),HelpFile); fprintf(hpj,"%s\n",buffer); } fprintf(hpj,"\n"); } } } } } char *PrintPhrase(unsigned int PhraseNum,char *out) { char *ptr; char *end; if(PhraseNum>PhraseCount) { error("phrase %u does not exist %u\n",PhraseNum,PhraseCount); return out; } if(NewPhrases) { ptr=NewPhrases+Offsets[PhraseNum]; end=NewPhrases+Offsets[PhraseNum+1]; } else { ptr=(char *)Offsets+Offsets[PhraseNum]; end=(char *)Offsets+Offsets[PhraseNum+1]; } while(ptr<end) { if(out) { *out++=*ptr++; } else if(isprint((unsigned char)*ptr)) { putchar(*ptr++); } else { printf("(%02x)",*(unsigned char *)ptr++); } } if(out) *out='\0'; return out; } BOOL GetBit(FILE *f) { static unsigned long mask; static unsigned long value; if(f) { mask<<=1; if(!mask) { value=GetDWord((MFILE *)f); mask=1L; } } else { mask=0L; // initialize } return (value&mask)!=0L; } BOOL PhraseLoad(FILE *HelpFile) { FILEHEADER FileHdr; char junk[30]; BOOL newphrases; PHRINDEXHDR PhrIndexHdr; unsigned int n; long l,offset; char *ptr; long FileStart,SavePos; if(SearchFile(HelpFile,"|Phrases",&FileStart)) { fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); offset=ftell(HelpFile); PhraseCount=getw(HelpFile); newphrases=PhraseCount==0x0800; if(newphrases) PhraseCount=getw(HelpFile); if(getw(HelpFile)!=0x0100) { fprintf(stderr,"Phrases file structure unknown\n"); return FALSE; } if(PhraseCount) { if(before31) { Offsets=myMalloc(FileHdr.FileSize-4); myFRead(Offsets,FileHdr.FileSize-4,HelpFile); } else { myFRead(&l,sizeof(l),HelpFile); if(newphrases) myFRead(&junk,sizeof(junk),HelpFile); Offsets=myMalloc(2*(PhraseCount+1)+l); ptr=(char *)(Offsets+PhraseCount+1); myFRead(Offsets,2*(PhraseCount+1),HelpFile); n=LzExpand((MFILE *)HelpFile,FileHdr.FileSize-(ftell(HelpFile)-offset),NULL,ptr); if(n!=l) { error("Phrases decompressed into %u instead %ld\n",n,l); } } printf("%u phrases loaded\n",PhraseCount); } return TRUE; } else if(SearchFile(HelpFile,"|PhrIndex",&FileStart)) { fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); myFRead(&PhrIndexHdr,sizeof(PhrIndexHdr),HelpFile); if(PhrIndexHdr.always4A01!=1&&PhrIndexHdr.always4A01!=0x4A01) fprintf(stderr,"PhrIndexHdr.always4A01=%04x\n",PhrIndexHdr.always4A01); if(PhrIndexHdr.always0!=0) fprintf(stderr,"PhrIndexHdr.always0=%04x\n",PhrIndexHdr.always0); if(PhrIndexHdr.always4A00!=0x4A00&&PhrIndexHdr.always4A00!=0x4A01&&PhrIndexHdr.always4A00!=0x4A02) fprintf(stderr,"PhrIndexHdr.always4A00=%04x\n",PhrIndexHdr.always4A00); SavePos=ftell(HelpFile); if(SearchFile(HelpFile,"|PhrImage",&FileStart)) { fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); if(FileHdr.FileSize!=PhrIndexHdr.phrimagecompressedsize) { fprintf(stderr,"PhrImage FileSize %ld, in PhrIndex.FileHdr %ld\n",PhrIndexHdr.phrimagecompressedsize,FileHdr.FileSize); } PhraseCount=(unsigned int)PhrIndexHdr.entries; Offsets=myMalloc(2*(PhraseCount+1)); NewPhrases=myMalloc(PhrIndexHdr.phrimagesize); if(PhrIndexHdr.phrimagesize==PhrIndexHdr.phrimagecompressedsize) { myFRead(NewPhrases,PhrIndexHdr.phrimagesize,HelpFile); } else { n=LzExpand((MFILE *)HelpFile,FileHdr.FileSize,NULL,NewPhrases); if(n!=PhrIndexHdr.phrimagesize) { fprintf(stderr,"PhrImage Size %ld, in PhrIndex %u\n",PhrIndexHdr.phrimagesize,n); } } fseek(HelpFile,SavePos,SEEK_SET); GetBit(NULL); offset=0; Offsets[0]=offset; for(l=0;l<PhrIndexHdr.entries;l++) { for(n=1;GetBit(HelpFile);n+=1<<PhrIndexHdr.bits) ; if(GetBit(HelpFile)) n+=1; if(PhrIndexHdr.bits>1) if(GetBit(HelpFile)) n+=2; if(PhrIndexHdr.bits>2) if(GetBit(HelpFile)) n+=4; if(PhrIndexHdr.bits>3) if(GetBit(HelpFile)) n+=8; if(PhrIndexHdr.bits>4) if(GetBit(HelpFile)) n+=16; offset+=n; Offsets[(int)l+1]=offset; } } printf("%u phrases loaded\n",PhraseCount); return TRUE; } return FALSE; } void PhraseDump(void) { unsigned int n; for(n=0;n<PhraseCount;n++) { printf("%-5d - ",n); PrintPhrase(n,NULL); printf("\n"); } } void PhraseList(char *FileName) { FILE *f; unsigned int n; static char buffer[512]; // max. length of a phrase 512 char if(PhraseCount) { f=myFOpen(FileName,"wt"); if(f) { for(n=0;n<PhraseCount;n++) { PrintPhrase(n,buffer); if(strlen(buffer)>=sizeof(buffer)) { error("Buffer overflow\n"); } fprintf(f,"%s\n",buffer); } myFClose(f); } } } void FontDump(FILE *HelpFile,long FileStart) { FILEHEADER FileHdr; FONTHEADER FontHdr; NEWFONT NewFont; FONTDESCRIPTOR FontDesc; int i,n; char FontName[80]; // Go to the FONT file and get the headers fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); FileStart=ftell(HelpFile); myFRead(&FontHdr,sizeof(FontHdr),HelpFile); printf("|FONTS\n\n Number Fonts: %d\n",FontHdr.NumFacenames); printf("Font # - Font Name\n"); n=(FontHdr.DescriptorsOffset-FontHdr.FacenamesOffset)/FontHdr.NumFacenames; for(i=0;i<FontHdr.NumFacenames;i++) { fseek(HelpFile,FileStart+FontHdr.FacenamesOffset+n*i,SEEK_SET); StringRead(FontName,sizeof(FontName),HelpFile); printf(" %3d - %.20s\n",i,FontName); } printf("Num Font Descriptors: %d\n\n",FontHdr.NumDescriptors); printf("Attributes: n=none b=bold i=ital u=undr s=strkout d=dblundr C=smallcaps\n\n"); printf("Font Name PointSize Family FG RGB BG RGB Attributes\n"); printf("--------------------------------------------------------------------------\n"); if(FontHdr.FacenamesOffset>=12) { for(i=0;i<FontHdr.NumDescriptors;i++) { fseek(HelpFile,FileStart+FontHdr.DescriptorsOffset+sizeof(NewFont)*i,SEEK_SET); myFRead(&NewFont,sizeof(NewFont),HelpFile); fseek(HelpFile,FileStart+FontHdr.FacenamesOffset+n*NewFont.FontName,SEEK_SET); StringRead(FontName,sizeof(FontName),HelpFile); printf("%02x %-20.20s %5ld %5d ",NewFont.unknown1,FontName,NewFont.Height,NewFont.Weight); printf("0x%02x%02x%02x ",NewFont.FGRGB[2],NewFont.FGRGB[1],NewFont.FGRGB[0]); printf("0x%02x%02x%02x ",NewFont.BGRGB[2],NewFont.BGRGB[1],NewFont.BGRGB[0]); if(NewFont.Italic) putchar('i'); if(NewFont.Underline) putchar('u'); if(NewFont.StrikeOut) putchar('s'); if(NewFont.DoubleUnderline) putchar('d'); if(NewFont.SmallCaps) putchar('C'); printf("\n"); } } else { for(i=0;i<FontHdr.NumDescriptors;i++) { fseek(HelpFile,FileStart+FontHdr.DescriptorsOffset+sizeof(FontDesc)*i,SEEK_SET); myFRead(&FontDesc,sizeof(FontDesc),HelpFile); fseek(HelpFile,FileStart+FontHdr.FacenamesOffset+n*FontDesc.FontName,SEEK_SET); StringRead(FontName,sizeof(FontName),HelpFile); printf("%-20.20s %4.1f ",FontName,(float)(FontDesc.HalfPoints / 2)); switch(FontDesc.FontFamily) { case FAM_MODERN: printf("Modern"); break; case FAM_ROMAN: printf("Roman "); break; case FAM_SWISS: printf("Swiss "); break; case FAM_SCRIPT: printf("Script"); break; case FAM_DECOR: printf("Decor "); break; default: printf("0X%02X ",FontDesc.FontFamily); break; } printf("0x%02x%02x%02xX ",FontDesc.FGRGB[2],FontDesc.FGRGB[1],FontDesc.FGRGB[0]); printf("0x%02x%02x%02xX ",FontDesc.BGRGB[2],FontDesc.BGRGB[1],FontDesc.BGRGB[0]); if(FontDesc.Attributes==0) putchar('n'); if(FontDesc.Attributes&FONT_BOLD) putchar('b'); if(FontDesc.Attributes&FONT_ITAL) putchar('i'); if(FontDesc.Attributes&FONT_UNDR) putchar('u'); if(FontDesc.Attributes&FONT_STRK) putchar('s'); if(FontDesc.Attributes&FONT_DBUN) putchar('d'); if(FontDesc.Attributes&FONT_SMCP) putchar('C'); printf("\n"); } } if(FontHdr.FacenamesOffset>=12) for(i=0;i<FontHdr.NumFormats;i++) { fseek(HelpFile,FileStart+FontHdr.FormatsOffset+0x91*i,SEEK_SET); myFRead(buffer,0x91,HelpFile); for(n=0;n<0x50;n++) { if(n%16==0) printf("%04x:",n); printf(" %02x",(unsigned char)buffer[n]); if(n%16==15) printf("\n"); } printf("%s\n",buffer+0x50); } } void FontLoad(FILE *HelpFile,FILE *rtf) { static char *familyname[]={"swiss","modern","roman","swiss","script","decor"}; FILEHEADER FileHdr; FONTHEADER FontHdr; char FontName[80]; long FileStart; long FontStart; int i,n; struct { unsigned char r,g,b; } color[128]; unsigned char *family; int colors; if(SearchFile(HelpFile,"|FONT",&FileStart)) { fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); FontStart=ftell(HelpFile); myFRead(&FontHdr,sizeof(FontHdr),HelpFile); fontnames=FontHdr.NumFacenames; n=(FontHdr.DescriptorsOffset-FontHdr.FacenamesOffset)/fontnames; fontname=myMalloc(fontnames*sizeof(char *)); for(i=0;i<fontnames;i++) { fseek(HelpFile,FontStart+FontHdr.FacenamesOffset+n*i,SEEK_SET); StringRead(FontName,sizeof(FontName),HelpFile); fontname[i]=myStrDup(FontName); } fseek(HelpFile,FontStart+FontHdr.DescriptorsOffset,SEEK_SET); if(FontHdr.FacenamesOffset>=12) { newfonts=FontHdr.NumDescriptors; newfont=myMalloc(newfonts*sizeof(NEWFONT)); myFRead(newfont,sizeof(NEWFONT)*newfonts,HelpFile); family=myMalloc(fontnames*sizeof(unsigned char)); for(i=0;i<fontnames;i++) family[i]=0; colors=1; // auto color[0].r=1; color[0].g=1; color[0].b=0; scaling=1; for(i=0;i<FontHdr.NumDescriptors;i++) { family[newfont[i].FontName]=newfont[i].PitchAndFamily>>4; for(n=0;n<colors;n++) { if(newfont[i].FGRGB[0]==color[n].r&&newfont[i].FGRGB[1]==color[n].g&&newfont[i].FGRGB[2]==color[n].b) break; } if(n==colors) { color[colors].r=newfont[i].FGRGB[0]; color[colors].g=newfont[i].FGRGB[1]; color[colors].b=newfont[i].FGRGB[2]; colors++; } newfont[i].FGRGB[0]=n; } } else { fonts=FontHdr.NumDescriptors; font=myMalloc(fonts*sizeof(FONTDESCRIPTOR)); myFRead(font,sizeof(FONTDESCRIPTOR)*fonts,HelpFile); family=myMalloc(fontnames*sizeof(unsigned char)); for(i=0;i<fontnames;i++) family[i]=0; colors=1; // auto color[0].r=1; color[0].g=1; color[0].b=0; for(i=0;i<FontHdr.NumDescriptors;i++) { family[font[i].FontName]=font[i].FontFamily; for(n=0;n<colors;n++) { if(font[i].FGRGB[0]==color[n].r&&font[i].FGRGB[1]==color[n].g&&font[i].FGRGB[2]==color[n].b) break; } if(n==colors) { color[colors].r=font[i].FGRGB[0]; color[colors].g=font[i].FGRGB[1]; color[colors].b=font[i].FGRGB[2]; colors++; } font[i].FGRGB[0]=n; } } if(rtf) { fprintf(rtf,"{\\fonttbl"); for(n=0;n<fontnames;n++) fprintf(rtf,"{\\f%d\\f%s %s;}",n,familyname[family[n]],fontname[n]); fprintf(rtf,"}\n"); if(colors>1) { fprintf(rtf,"{\\colortbl;"); for(n=1;n<colors;n++) fprintf(rtf,"\\red%d\\green%d\\blue%d;",color[n].r,color[n].g,color[n].b); fprintf(rtf,"}\n"); } } printf("%u font names, %u font descriptors loaded\n",fontnames,FontHdr.NumDescriptors); } } void ToMapLoad(FILE *HelpFile) { FILEHEADER FileHdr; long FileStart; if(SearchFile(HelpFile,"|TOMAP",&FileStart)) { fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); if(FileHdr.FileSize!=(size_t)FileHdr.FileSize) { error("Too many Topics in |TOMAP\n"); exit(1); } Topics=(int)(FileHdr.FileSize/sizeof(long)); Topic=myMalloc(FileHdr.FileSize); myFRead(Topic,FileHdr.FileSize,HelpFile); } } void ToMapDump(FILE *HelpFile,long FileStart) { FILEHEADER FileHdr; long TopicOffset,i; fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); printf("Topic # - Topic Start\n"); for(i=0;i*4L<FileHdr.FileSize;i++) { myFRead(&TopicOffset,sizeof(TopicOffset),HelpFile); printf("%-12ld - 0x%08lX\n",i,TopicOffset); } } long TopicRead(FILE *HelpFile,long TopicAddress,void *dest,long NumBytes) { static FILEHEADER FileHdr; static long NextTopic; static long BlockNum; long NewBlockNum; unsigned char *Dest; unsigned int BytesToRead; static unsigned int DecompSize; static unsigned int offset; TOPICLINK *TopicLink; unsigned int BytesRead; static long BytesInBlock; long here; Dest=dest; if(TopicAddress) { if(!TopicFileStart) { myFRead(&FileHdr,sizeof(FileHdr),HelpFile); TopicFileStart=ftell(HelpFile); NextTopic=12L; BlockNum=-1L; } NewBlockNum=TopicAddress/DecompressSize; if(BlockNum!=NewBlockNum) { if(NewBlockNum*TopicBlockSize>=FileHdr.FileSize) return 0; BlockNum=NewBlockNum; TopicOffset=BlockNum*0x8000L; dontCount=TRUE; fseek(HelpFile,TopicFileStart+TopicBlockSize*NewBlockNum,SEEK_SET); BytesToRead=TopicBlockSize; if(FileHdr.FileSize-TopicBlockSize*NewBlockNum<BytesToRead) { BytesToRead=(unsigned int)(FileHdr.FileSize-TopicBlockSize*NewBlockNum); } myFRead(buffer,sizeof(TOPICBLOCKHEADER),HelpFile); BytesToRead-=sizeof(TOPICBLOCKHEADER); if(lzcompressed) { DecompSize=LzExpand((MFILE *)HelpFile,BytesToRead,NULL,buffer+sizeof(TOPICBLOCKHEADER)); } else { DecompSize=myFRead(buffer+sizeof(TOPICBLOCKHEADER),BytesToRead,HelpFile); } DecompSize+=sizeof(TOPICBLOCKHEADER); } offset=(unsigned int)(TopicAddress%DecompressSize); if(offset>=DecompSize) return 0L; TopicLink=(TOPICLINK *)(buffer+offset); if(before31) { NextTopic=TopicLink->NextBlock+TopicAddress; } else { NextTopic=TopicLink->NextBlock; } BytesInBlock=TopicLink->BlockSize; } here=BlockNum*DecompressSize+offset; for(BytesRead=0;BytesRead<NumBytes;BytesRead++) { if(BytesInBlock<=0) { return TopicRead(HelpFile,NextTopic,Dest,NumBytes-BytesRead)+BytesRead; } if(offset>=DecompSize) { NewBlockNum=BlockNum+1; if(NewBlockNum*TopicBlockSize>=FileHdr.FileSize) break; fseek(HelpFile,TopicFileStart+TopicBlockSize*NewBlockNum,SEEK_SET); BlockNum=NewBlockNum; TopicOffset=BlockNum*0x8000L; dontCount=TRUE; BytesToRead=TopicBlockSize; if(FileHdr.FileSize-TopicBlockSize*NewBlockNum<BytesToRead) { BytesToRead=(unsigned int)(FileHdr.FileSize-TopicBlockSize*NewBlockNum); } myFRead(buffer,sizeof(TOPICBLOCKHEADER),HelpFile); BytesToRead-=sizeof(TOPICBLOCKHEADER); if(lzcompressed) { DecompSize=LzExpand((MFILE *)HelpFile,BytesToRead,NULL,buffer+sizeof(TOPICBLOCKHEADER)); } else { DecompSize=myFRead(buffer+sizeof(TOPICBLOCKHEADER),BytesToRead,HelpFile); } DecompSize+=sizeof(TOPICBLOCKHEADER); offset=sizeof(TOPICBLOCKHEADER); } *Dest++=buffer[offset++]; BytesInBlock--; } TopicPos=here; return BytesRead; } #ifndef __TURBOC__ void textcolor(int color) { } #endif void ListTopic(FILE *HelpFile) { long FileStart,BlockNum,BufferSize; unsigned int BytesToRead,DecompSize; FILEHEADER FileHdr; TOPICLINK *TopicLink; long NextTopicLink,NextLinkData1,NextLinkData2,NextBlockEnd; int i,j,n,color; if(SearchFile(HelpFile,"|TOPIC",&FileStart)) { if(before31) { BufferSize=TopicBlockSize; } else { BufferSize=0x4000; } NextTopicLink=12; fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); printf("|TOPIC FileSize: 0x%08lX, TopicBlockSize=%u\n",FileHdr.FileSize,TopicBlockSize); textcolor(4); cprintf("TOPICBLOCKHEADER "); textcolor(2); cprintf("TOPICLINK "); textcolor(5); cprintf("LinkData1 "); textcolor(3); cprintf("LinkData2 "); textcolor(1); cprintf("unused\r\n"); textcolor(7); color=2; NextTopicLink=12; for(BlockNum=0;BlockNum*TopicBlockSize<FileHdr.FileSize;BlockNum++) { BytesToRead=TopicBlockSize; if(FileHdr.FileSize-TopicBlockSize*BlockNum<BytesToRead) { BytesToRead=(unsigned int)(FileHdr.FileSize-TopicBlockSize*BlockNum); } myFRead(buffer,sizeof(TOPICBLOCKHEADER),HelpFile); cprintf("----------------------------------------------------------------------------\r\n"); BytesToRead-=sizeof(TOPICBLOCKHEADER); if(lzcompressed) { DecompSize=LzExpand((MFILE *)HelpFile,BytesToRead,NULL,buffer+sizeof(TOPICBLOCKHEADER)); } else { DecompSize=myFRead(buffer+sizeof(TOPICBLOCKHEADER),BytesToRead,HelpFile); } DecompSize+=sizeof(TOPICBLOCKHEADER); if(((long *)buffer)[1]>BufferSize*BlockNum) NextTopicLink=((long *)buffer)[1]%BufferSize; for(i=0;i<DecompSize;i+=16) { cprintf("0x%08lX: ",BufferSize*BlockNum+i); textcolor(color); n=(int)(DecompSize-i>16?16:DecompSize-i); for(j=0;j<n;j++) { if(i+j==0) textcolor(4); if(i+j==12) textcolor(color); if(i+j==NextTopicLink) { textcolor(color=2); TopicLink=(TOPICLINK *)(buffer+i+j); NextLinkData1=NextTopicLink+21; NextLinkData2=NextTopicLink+TopicLink->DataLen1; NextBlockEnd=NextTopicLink+TopicLink->BlockSize; if(NextLinkData1>NextBlockEnd) NextLinkData1=NextBlockEnd; if(NextLinkData2>NextBlockEnd) NextLinkData2=NextBlockEnd; NextTopicLink+=TopicLink->BlockSize; } if(i+j==NextLinkData1) textcolor(color=5); if(i+j==NextLinkData2) textcolor(color=3); if(i+j==NextBlockEnd) textcolor(color=1); cprintf("%02X ",(unsigned char)buffer[i+j]); } textcolor(7); for(j=0;j<16-n;j++) cprintf(" "); for(j=0;j<n;j++) cprintf("%c",isprint((unsigned char)buffer[i+j])?buffer[i+j]:'.'); printf("\n"); } NextTopicLink-=BufferSize-12; NextLinkData1-=BufferSize-12; NextLinkData2-=BufferSize-12; NextBlockEnd-=BufferSize-12; } } } /************************************************** Phrase replacement of a string from topic record. Expands to out, returns end of expanded string. Expands to stdout if out==NULL, returns NULL then. ****************************************************/ char *StringPrint(unsigned char *String,long Length,char *out) { int CurChar; if(NewPhrases) { while(Length) { CurChar=*String++; Length--; if((CurChar&1)==0) // phrases 0..127 { out=PrintPhrase(CurChar/2,out); } else if((CurChar&3)==1) // phrases 128..16511 { CurChar=128+(CurChar/4)*256+*String++; Length--; out=PrintPhrase(CurChar,out); } else if((CurChar&7)==3) // copy next n characters { while(CurChar>0) { if(out) { *out++=*String++; } else if(isprint((unsigned char)*String)) { putchar(*String++); } else { printf("(%02x)",*String++); } Length--; CurChar-=8; } } else if((CurChar&0x0F)==0x07) { while(CurChar>0) { if(out) { *out++=' '; } else { printf(" "); } CurChar-=16; } } else // if((CurChar&0x0F)==0x0F) { while(CurChar>0) { if(out) { *out++='\0'; } else { printf("(00)"); } CurChar-=16; } } } } else { while(Length) { CurChar=*String++; Length--; if(CurChar>0&&CurChar<16) // phrase 0..1919 { CurChar=256*(CurChar-1)+*String++; Length--; out=PrintPhrase(CurChar/2,out); if(CurChar&1) { if(out) { *out++=' '; } else { putchar(' '); } } } else if(out) { *out++=CurChar; } else if(isprint((unsigned char)CurChar)) { putchar(CurChar); } else { printf("(%02x)",CurChar); } } } if(out) *out='\0'; return out; } void putrtf(FILE *rtf,char *str) { if(rtf) while(*str) { if(*str=='{'||*str=='}'||*str=='\\') { putc('\\',rtf); putc(*str++,rtf); } else if(isprint((unsigned char)*str)) { putc(*str++,rtf); } else { fprintf(rtf,"\\'%02x",(unsigned char)*str++); } } } int NextKeywordRec,KeywordRecs; typedef struct { char Footnote; char *Keyword; long TopicOffset; } KEYWORDREC; KEYWORDREC *KeywordRec; int KeywordRecCmp(const void *a,const void *b) { const KEYWORDREC *A; const KEYWORDREC *B; A=(const KEYWORDREC *)a; B=(const KEYWORDREC *)b; if(A->TopicOffset<B->TopicOffset) return -1; if(A->TopicOffset>B->TopicOffset) return 1; return 0; } long NextTopicOffset(FILE *HelpFile,long topic) { long pos,FileStart; char Title[256]; int entries,count; long TopicOffset; BUFFER buf; pos=ftell(HelpFile); if(SearchFile(HelpFile,"|TTLBTREE",&FileStart)) { entries=GetFirstPage(HelpFile,FileStart,&buf); while(entries) { for(count=1;count<=entries;count++) { myFRead(&TopicOffset,sizeof(TopicOffset),HelpFile); if(TopicOffset>topic) { fseek(HelpFile,pos,SEEK_SET); return TopicOffset; } StringRead(Title,sizeof(Title),HelpFile); } entries=GetNextPage(HelpFile,&buf); } } fseek(HelpFile,pos,SEEK_SET); return 0x7FFFFFFFL; } void CollectKeywords(FILE *HelpFile,long from,long upto) { FILEHEADER FileHdr; unsigned short count,i; long FileStart,TreeStart,savepos; char Keyword[256]; // variable length keyword short Count; // count of keywords occurances long KWDataOffset; // offset into |KWDATA file long *keytopic; BUFFER buf; short entries; int map,n; char data[10]; char tree[10]; if(KeywordRecs&&KeywordRec) // free old keywords { for(i=0;i<KeywordRecs;i++) { if(KeywordRec[i].Keyword) free(KeywordRec[i].Keyword); } free(KeywordRec); KeywordRec=NULL; NextKeywordRec=KeywordRecs=0; } savepos=ftell(HelpFile); for(n=0;n<2;n++) for(map='A';map<='z';map++) if(n==0&&lists[map-'A']||n==1&&keyindex[map-'A']) { if(n) { sprintf(data,"|%cKWDATA",map); sprintf(tree,"|%cKWBTREE",map); } else { sprintf(data,"|%cWDATA",map); sprintf(tree,"|%cWBTREE",map); } if(SearchFile(HelpFile,data,&FileStart)&&SearchFile(HelpFile,tree,&TreeStart)) { fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); keytopic=myMalloc(FileHdr.FileSize); myFRead(keytopic,FileHdr.FileSize,HelpFile); entries=GetFirstPage(HelpFile,TreeStart,&buf); while(entries) { for(count=1;count<=entries;count++) { StringRead(Keyword,sizeof(Keyword),HelpFile); myFRead(&Count,sizeof(Count),HelpFile); myFRead(&KWDataOffset,sizeof(KWDataOffset),HelpFile); for(i=0;i<Count;i++) { if(keytopic[KWDataOffset/4+i]>=from&&keytopic[KWDataOffset/4+i]<upto) { KeywordRec=myReAlloc(KeywordRec,(KeywordRecs+1)*sizeof(KEYWORDREC)); KeywordRec[KeywordRecs].Footnote=map; KeywordRec[KeywordRecs].Keyword=myStrDup(Keyword); KeywordRec[KeywordRecs].TopicOffset=keytopic[KWDataOffset/4+i]; KeywordRecs++; } } } entries=GetNextPage(HelpFile,&buf); } free(keytopic); } if(KeywordRec) qsort(KeywordRec,KeywordRecs,sizeof(KEYWORDREC),KeywordRecCmp); } fseek(HelpFile,savepos,SEEK_SET); } void ListKeywords(FILE *HelpFile,FILE *rtf,long topic) { FILEHEADER FileHdr; unsigned short count,i; long FileStart,TreeStart,savepos; char Keyword[256]; // variable length keyword short Count; // count of keywords occurances long KWDataOffset; // offset into |KWDATA file long *keytopic; BUFFER buf; short entries; int map,n; BOOL first; char data[10]; char tree[10]; savepos=ftell(HelpFile); for(n=0;n<2;n++) for(map='A';map<='z';map++) if(n==0&&lists[map-'A']||n==1&&keyindex[map-'A']) { if(n) { sprintf(data,"|%cKWDATA",map); sprintf(tree,"|%cKWBTREE",map); } else { sprintf(data,"|%cWDATA",map); sprintf(tree,"|%cWBTREE",map); } if(SearchFile(HelpFile,data,&FileStart)&&SearchFile(HelpFile,tree,&TreeStart)) { first=TRUE; fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); keytopic=myMalloc(FileHdr.FileSize); myFRead(keytopic,FileHdr.FileSize,HelpFile); entries=GetFirstPage(HelpFile,TreeStart,&buf); while(entries) { for(count=1;count<=entries;count++) { StringRead(Keyword,sizeof(Keyword),HelpFile); myFRead(&Count,sizeof(Count),HelpFile); myFRead(&KWDataOffset,sizeof(KWDataOffset),HelpFile); for(i=0;i<Count;i++) { if(keytopic[KWDataOffset/4+i]==topic) { if(first) { if(n) fprintf(rtf,"K"); fprintf(rtf,"%c{\\footnote %c ",map,map); first=FALSE; } else { fprintf(rtf,"; "); } putrtf(rtf,Keyword); } } } entries=GetNextPage(HelpFile,&buf); } if(!first) fprintf(rtf,"}\n"); free(keytopic); } } fseek(HelpFile,savepos,SEEK_SET); } void ListWindows(FILE *HelpFile,FILE *rtf,long topic) { long savepos; static int entries; static long offset,value; static BUFFER buf; long FileStart; static int VIOLAfound=-1; if(VIOLAfound==0) return; savepos=ftell(HelpFile); if(VIOLAfound==-1) { VIOLAfound=0; if(SearchFile(HelpFile,"|VIOLA",&FileStart)) { entries=GetFirstPage(HelpFile,FileStart,&buf); if(entries) { myFRead(&offset,sizeof(offset),HelpFile); myFRead(&value,sizeof(value),HelpFile); VIOLAfound=1; entries--; } } } if(VIOLAfound==1) { while(topic>offset) { if(!entries) { entries=GetNextPage(HelpFile,&buf); if(entries==0) { VIOLAfound=0; break; } } myFRead(&offset,sizeof(offset),HelpFile); myFRead(&value,sizeof(value),HelpFile); entries--; } if(offset==topic) { fprintf(rtf,">{\\footnote > %s}\n",WindowName(value)); } } fseek(HelpFile,savepos,SEEK_SET); } void AddStart(long StartTopic,int BrowseNum,int Count) { start=myReAlloc(start,(starts+1)*sizeof(START)); start[starts].StartTopic=StartTopic; start[starts].BrowseNum=BrowseNum; start[starts].Start=Count; starts++; } void FixStart(int BrowseNum,int NewBrowseNum,int AddCount) { int i; for(i=0;i<starts;i++) if(start[i].BrowseNum==BrowseNum) { start[i].BrowseNum=NewBrowseNum; start[i].Start+=AddCount; } } void AddBrowse(long StartTopic,long NextTopic,long PrevTopic) { int i; for(i=0;i<browses;i++) if(browse[i].StartTopic==-1L) break; // empty space in array ? if(i==browses) // no empty space, add to array { browse=myReAlloc(browse,++browses*sizeof(BROWSE)); } browse[i].StartTopic=StartTopic; browse[i].NextTopic=NextTopic; browse[i].PrevTopic=PrevTopic; browse[i].BrowseNum=browsenums++; browse[i].Start=1; browse[i].Count=1; } void MergeBrowse(long TopicOffset,long NextTopic,long PrevTopic) { int i,j; for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L) { if(browse[i].NextTopic==TopicOffset) break; } for(j=0;j<browses;j++) if(browse[j].StartTopic!=-1L) { if(browse[j].PrevTopic==TopicOffset) break; } if(i<browses&&j<browses) { browse[i].Count++; browse[i].NextTopic=browse[j].NextTopic; FixStart(browse[j].BrowseNum,browse[i].BrowseNum,browse[i].Count); browse[j].Start+=browse[i].Count; AddStart(browse[j].StartTopic,browse[i].BrowseNum,browse[j].Start); browse[i].Count+=browse[j].Count; browse[j].StartTopic=-1L; if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L) { AddStart(browse[i].StartTopic,browse[i].BrowseNum,browse[i].Start); browse[i].StartTopic=-1L; } } else { warnings=TRUE; if(warn) fprintf(stderr,"\nCan not merge %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic); } } void LinkBrowse(long TopicOffset,long NextTopic,long PrevTopic) { int i; for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L) { if(browse[i].NextTopic==TopicOffset) break; } if(i<browses) { browse[i].NextTopic=NextTopic; browse[i].Count++; if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L) { AddStart(browse[i].StartTopic,browse[i].BrowseNum,browse[i].Start); browse[i].StartTopic=-1L; } } else { warnings=TRUE; if(warn) { fprintf(stderr,"\nCan not link %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic); for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L) { fprintf(stderr,"Open %08lx %08lx\n",browse[i].PrevTopic,browse[i].NextTopic); } } } } void BackLinkBrowse(long TopicOffset,long NextTopic,long PrevTopic) { int i; for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L) { if(browse[i].PrevTopic==TopicOffset) break; } if(i<browses) { browse[i].PrevTopic=PrevTopic; browse[i].Count++; browse[i].Start++; FixStart(browse[i].BrowseNum,browse[i].BrowseNum,1); if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L) { AddStart(browse[i].StartTopic,browse[i].BrowseNum,browse[i].Start); browse[i].StartTopic=-1L; } } else { warnings=TRUE; if(warn) fprintf(stderr,"\nCan not backlink %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic); } } unsigned long AddLink(long StartTopic,long NextTopic,long PrevTopic) { int i,j; unsigned long result; result=0L; for(i=0;i<browses;i++) if(browse[i].StartTopic==-1L) break; if(i==browses) browse=myReAlloc(browse,++browses*sizeof(BROWSE)); for(j=0;j<starts;j++) if(start[j].StartTopic==StartTopic) break; if(j<starts) { browse[i].StartTopic=start[j].StartTopic; browse[i].BrowseNum=start[j].BrowseNum; browse[i].Start=start[j].Start; browse[i].Count=start[j].Start; browse[i].NextTopic=NextTopic; browse[i].PrevTopic=PrevTopic; result=browse[i].BrowseNum+((long)browse[i].Start<<16); } else { warnings=TRUE; if(warn) fprintf(stderr,"\nBrowse start %08lx not found\n",StartTopic); } return result; } unsigned long MergeLink(long TopicOffset,long NextTopic,long PrevTopic) { int i,j; unsigned long result; result=0L; for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L) { if(browse[i].NextTopic==TopicOffset) break; } for(j=0;j<browses;j++) if(browse[j].StartTopic!=-1L) { if(browse[j].PrevTopic==TopicOffset) break; } if(i<browses&&j<browses) { browse[i].Count++; browse[j].Start--; if(browse[i].Count!=browse[j].Start) { warnings=TRUE; if(warn) fprintf(stderr,"\nPrev browse end %d doen't match next browse start %d\n",browse[i].Count,browse[j].Start); } result=browse[i].BrowseNum+((long)browse[i].Count<<16); browse[i].NextTopic=browse[j].NextTopic; browse[i].Count=browse[j].Count; browse[j].StartTopic=-1L; } else { warnings=TRUE; if(warn) fprintf(stderr,"\nCan not merge %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic); } return result; } unsigned long LinkLink(long TopicOffset,long NextTopic,long PrevTopic) { int i; unsigned long result; result=0L; for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L) { if(browse[i].NextTopic==TopicOffset) break; } if(i<browses) { browse[i].NextTopic=NextTopic; browse[i].Count++; result=browse[i].BrowseNum+((long)browse[i].Count<<16); if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L) { browse[i].StartTopic=-1L; } } else { warnings=TRUE; if(warn) fprintf(stderr,"\nCan not link %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic); } return result; } unsigned long BackLinkLink(long TopicOffset,long NextTopic,long PrevTopic) { int i; unsigned long result; result=0L; for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L) { if(browse[i].PrevTopic==TopicOffset) break; } if(i<browses) { browse[i].PrevTopic=PrevTopic; browse[i].Start--; result=browse[i].BrowseNum+((long)browse[i].Start<<16); if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L) { browse[i].StartTopic=-1L; } } else { warnings=TRUE; if(warn) fprintf(stderr,"\nCan not backlink %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic); } return result; } char *scanint(char *ptr,short *val) { if(*ptr&1) { *val=(*(unsigned short *)(ptr)>>1)-0x4000; ptr+=2; } else { *val=(*(unsigned char *)(ptr)>>1)-0x40; ptr++; } return ptr; } char *scanword(char *ptr,unsigned short *val) { if(*ptr&1) { *val=*(unsigned short *)(ptr)>>1; ptr+=2; } else { *val=*(unsigned char *)(ptr)>>1; ptr++; } return ptr; } char *scanlong(char *ptr,long *val) { if(*(short *)ptr&1) { *val=(*(unsigned long *)(ptr)>>1)-0x40000000L; ptr+=4; } else { *val=(*(unsigned short *)(ptr)>>1)-0x4000; ptr+=2; } return ptr; } char *scandword(char *ptr,unsigned long *val) { if(*(short *)ptr&1) { *val=*(unsigned long *)(ptr)>>1; ptr+=2; } else { *val=*(unsigned short *)(ptr)>>1; ptr+=2; } return ptr; } void ChangeFont(FILE *rtf,int i,BOOL ul,BOOL uldb) { if(i>=0) { if(fonts&&i<fonts) { fprintf(rtf,"}{\\f%d",font[i].FontName); if(font[i].Attributes&FONT_ITAL) fprintf(rtf,"\\i"); if(font[i].Attributes&FONT_BOLD) fprintf(rtf,"\\b"); if(ul||(font[i].Attributes&FONT_UNDR)) fprintf(rtf,"\\ul"); if(font[i].Attributes&FONT_STRK) fprintf(rtf,"\\strike"); if(uldb||(font[i].Attributes&FONT_DBUN)) fprintf(rtf,"\\uldb"); if(font[i].Attributes&FONT_SMCP) fprintf(rtf,"\\scaps"); fprintf(rtf,"\\fs%d",font[i].HalfPoints); fprintf(rtf,"\\cf%d ",font[i].FGRGB[0]); } else if(newfonts&&i<newfonts) { fprintf(rtf,"}{\\f%d",newfont[i].FontName); if(newfont[i].Italic) fprintf(rtf,"\\i"); if(newfont[i].Weight>600) fprintf(rtf,"\\b"); if(ul||newfont[i].Underline) fprintf(rtf,"\\ul"); if(newfont[i].StrikeOut) fprintf(rtf,"\\strike"); if(uldb||newfont[i].DoubleUnderline) fprintf(rtf,"\\uldb"); if(newfont[i].SmallCaps) fprintf(rtf,"\\scaps"); fprintf(rtf,"\\fs%ld",-2*newfont[i].Height); fprintf(rtf,"\\cf%d ",newfont[i].FGRGB[0]); } } } void TopicDump(FILE *HelpFile,FILE *rtf,BOOL doc) { TOPICLINK TopicLink; TOPICLINK NextTopicLink; char *LinkData1; // Data associated with this link long nonscroll=-1L; char *LinkData2; // Second set of data char *end; int fontset; long FileOffset; int NextContextRec; unsigned long BrowseNum; int BytesRead; long counter=0L; char *hotspot; char *arg; int i; long offset; long pos; BOOL firsttopic=TRUE; BOOL ul,uldb; int nextbitmap; long TopicNum; if(SearchFile(HelpFile,"|TOPIC",&FileOffset)) { fseek(HelpFile,FileOffset,SEEK_SET); dontCount=FALSE; fontset=-1; nextbitmap=1; if(browse) free(browse); browse=NULL; browses=0; NextContextRec=0; TopicNum=16; ul=uldb=FALSE; hotspot=NULL; BytesRead=(int)TopicRead(HelpFile,12L,&TopicLink,sizeof(TopicLink)); while(BytesRead==sizeof(TOPICLINK)) { pos=TopicPos; offset=TopicOffset; if(TopicLink.DataLen1>sizeof(TOPICLINK)) { LinkData1=myMalloc(TopicLink.DataLen1-sizeof(TOPICLINK)+1); TopicRead(HelpFile,0L,LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK)); } else { LinkData1=NULL; } if(TopicLink.DataLen1<TopicLink.BlockSize) // read LinkData2 without phrase replacement { LinkData2=myMalloc(TopicLink.BlockSize-TopicLink.DataLen1+1); TopicRead(HelpFile,0L,LinkData2,TopicLink.BlockSize-TopicLink.DataLen1); } else { LinkData2=NULL; } BytesRead=(int)TopicRead(HelpFile,0L,&NextTopicLink,sizeof(NextTopicLink)); if(LinkData1&&TopicLink.RecordType==TL_TOPICHDR) // display a Topic Header record { if(!firsttopic) fprintf(rtf,"\\page\n"); firsttopic=FALSE; fprintf(stderr,"."); if(!doc) { BrowseNum=0L; if(before31) { TOPICHEADER30 *TopicHdr; TopicHdr=(TOPICHEADER30 *)LinkData1; fprintf(rtf,"{#{\\footnote # TOPIC%ld}}\n",TopicNum); if(resolvebrowse) { if(TopicHdr->NextTopicNum>TopicNum&&TopicHdr->PrevTopicNum>TopicNum || TopicHdr->NextTopicNum==-1&&TopicHdr->PrevTopicNum>TopicNum || TopicHdr->NextTopicNum>TopicNum&&TopicHdr->PrevTopicNum==-1) { BrowseNum=AddLink(TopicNum,TopicHdr->NextTopicNum,TopicHdr->PrevTopicNum); } else if(TopicHdr->NextTopicNum!=-1&&TopicHdr->NextTopicNum<TopicNum&&TopicHdr->PrevTopicNum!=-1&&TopicHdr->PrevTopicNum<TopicNum) { BrowseNum=MergeLink(TopicNum,TopicHdr->NextTopicNum,TopicHdr->PrevTopicNum); } else if(TopicHdr->NextTopicNum!=-1&&TopicHdr->NextTopicNum<TopicNum&&(TopicHdr->PrevTopicNum==-1||TopicHdr->PrevTopicNum>TopicNum)) { BrowseNum=BackLinkLink(TopicNum,TopicHdr->NextTopicNum,TopicHdr->PrevTopicNum); } else if(TopicHdr->PrevTopicNum!=-1&&TopicHdr->PrevTopicNum<TopicNum&&(TopicHdr->NextTopicNum==-1||TopicHdr->NextTopicNum>TopicNum)) { BrowseNum=LinkLink(TopicNum,TopicHdr->NextTopicNum,TopicHdr->PrevTopicNum); } } ListKeywords(HelpFile,rtf,pos); TopicNum++; } else { TOPICHEADER *TopicHdr; TopicHdr=(TOPICHEADER *)LinkData1; if(TopicHdr->NonScroll!=-1L) { if(TopicHdr->Scroll!=-1L) { nonscroll=TopicHdr->Scroll; } else { nonscroll=TopicHdr->NextTopic; } } else { nonscroll=-1L; } pos=offset; // topic offset to link keywords to while(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset<offset) { warnings=TRUE; if(warn) fprintf(stderr,"\noffset %08lx behind %08lx\n",offset,ContextRec[NextContextRec].TopicOffset); fprintf(rtf,"{#{\\footnote # %s}}\n",unhash(ContextRec[NextContextRec].HashValue)); while(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset==ContextRec[NextContextRec+1].TopicOffset) { NextContextRec++; } NextContextRec++; } if(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset==offset) { fprintf(rtf,"{#{\\footnote # %s}}\n",unhash(ContextRec[NextContextRec].HashValue)); while(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset==ContextRec[NextContextRec+1].TopicOffset) { NextContextRec++; } NextContextRec++; } if(resolvebrowse) { if(TopicHdr->BrowseFor>offset&&TopicHdr->BrowseBck>offset || TopicHdr->BrowseFor==-1L&&TopicHdr->BrowseBck>offset || TopicHdr->BrowseFor>offset&&TopicHdr->BrowseBck==-1L) { BrowseNum=AddLink(offset,TopicHdr->BrowseFor,TopicHdr->BrowseBck); } else if(TopicHdr->BrowseFor!=-1L&&TopicHdr->BrowseFor<offset&&TopicHdr->BrowseBck!=-1L&&TopicHdr->BrowseBck<offset) { BrowseNum=MergeLink(offset,TopicHdr->BrowseFor,TopicHdr->BrowseBck); } else if(TopicHdr->BrowseFor!=-1L&&TopicHdr->BrowseFor<offset&&(TopicHdr->BrowseBck==-1L||TopicHdr->BrowseBck>offset)) { BrowseNum=BackLinkLink(offset,TopicHdr->BrowseFor,TopicHdr->BrowseBck); } else if(TopicHdr->BrowseBck!=-1L&&TopicHdr->BrowseBck<offset&&(TopicHdr->BrowseFor==-1L||TopicHdr->BrowseFor>offset)) { BrowseNum=LinkLink(offset,TopicHdr->BrowseFor,TopicHdr->BrowseBck); } } CollectKeywords(HelpFile,pos,NextTopicOffset(HelpFile,pos)); } if(BrowseNum) fprintf(rtf,"+{\\footnote + BROWSE%04x:%04x}\n",BrowseNum); if(LinkData2&&TopicLink.DataLen2>0) { char *q; unsigned i; if(TopicLink.DataLen2<=TopicLink.BlockSize-TopicLink.DataLen1) { end=LinkData2+TopicLink.DataLen2; } else { q=myMalloc(TopicLink.DataLen2+1); end=StringPrint(LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,q); free(LinkData2); LinkData2=q; if(end>LinkData2+TopicLink.DataLen2) { error("Phrase replacement delivers %u instead of %ld bytes\n",(unsigned int)(end-LinkData2),TopicLink.DataLen2); } while(end<LinkData2+TopicLink.DataLen2) *end++='\0'; } *end='\0'; fprintf(rtf,"${\\footnote $ "); putrtf(rtf,LinkData2); fprintf(rtf,"}\n"); for(i=strlen(LinkData2)+1;i<TopicLink.DataLen2;i+=strlen(LinkData2+i)+1) { fprintf(rtf,"!{\\footnote ! "); putrtf(rtf,LinkData2+i); fprintf(rtf,"}\n"); } } ListWindows(HelpFile,rtf,pos); } } else if(LinkData1&&LinkData2&&TopicLink.RecordType==TL_DISPLAY30||TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE) { int col,cols,lastcol; short *iptr; unsigned short x1,x2,x3,x4; short y1; long l1; char *ptr; char *cmd; char *end; char *q; unsigned int bits; if(!doc&&TopicLink.RecordType!=TL_DISPLAY30&&counter!=offset) { if((offset&0x3FFF)!=0||counter>offset) { warnings=TRUE; if(warn) fprintf(stderr,"\ncounter %08lx becomes %08lx\n",counter,offset); } counter=offset; } if(pos>=nonscroll) nonscroll=-1L; ptr=scanlong(LinkData1,&l1); switch(TopicLink.RecordType) { case TL_DISPLAY: ptr=scanword(ptr,&x3); if(!dontCount) TopicOffset+=x3; break; case TL_TABLE: ptr=scanword(ptr,&x3); if(!dontCount) TopicOffset+=x3; fprintf(rtf,"\\trowd "); cols=(unsigned char)*ptr++; x4=(unsigned char)*ptr++; switch(x4) { case 0: case 2: l1=*(short *)ptr; // min table width ptr+=2; fprintf(rtf,"\\trqc"); break; case 1: l1=32767L; break; case 3: l1=3276L; // scaling is 1 instead of 10 break; default: error("\nunknown column data modifier %02x found\n",x4); } iptr=(short *)ptr; if(cols>1) { x1=iptr[0]+iptr[1]+iptr[3]/2; fprintf(rtf,"\\trgaph%ld\\trleft%ld \\cellx%ld\\cellx%ld",(iptr[3]*l1)/3276,((iptr[1]-iptr[3])*l1-32767)/3276,(x1*l1)/3276,((x1+iptr[2]+iptr[3])*l1)/3276); x1+=iptr[2]+iptr[3]; for(col=2;col<cols;col++) { x1+=iptr[2*col]+iptr[2*col+1]; fprintf(rtf,"\\cellx%ld",(x1*l1)/3276); } } else { fprintf(rtf,"\\trleft%ld \\cellx%ld ",(iptr[1]*l1-32767)/3276,(iptr[0]*l1)/3276); } ptr=(char *)(iptr+2*cols); break; } // do phrase replacement of LinkData2 if(TopicLink.DataLen2<=TopicLink.BlockSize-TopicLink.DataLen1) { end=LinkData2+TopicLink.DataLen2; q=LinkData2; } else { q=myMalloc(TopicLink.DataLen2+1); end=StringPrint(LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,q); if(end>q+TopicLink.DataLen2) { error("phrase replacement delivers %u instead of %ld bytes\n",(unsigned int)(end-q),TopicLink.DataLen2); HexDumpMemory(LinkData2,TopicLink.BlockSize-TopicLink.DataLen1); StringPrint(LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,NULL); exit(1); } while(end<q+TopicLink.DataLen2) *end++='\0'; free(LinkData2); LinkData2=q; } *end='\0'; lastcol=-1; for(col=0;(TopicLink.RecordType==TL_TABLE?*(short *)ptr!=-1:col==0)&&ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK);col++) { fprintf(rtf,"\\pard "); if(nonscroll!=-1L) fprintf(rtf,"\\keepn "); if(TopicLink.RecordType==TL_TABLE) { fprintf(rtf,"\\intbl "); lastcol=*(short *)ptr; ptr+=5; } ptr+=4; bits=*(unsigned short *)ptr; ptr+=2; if(bits&0x0400) fprintf(rtf,"\\qr "); if(bits&0x0800) fprintf(rtf,"\\qc "); if(bits&0x0001) { ptr=scanlong(ptr,&l1); } if(bits&0x0002) { ptr=scanint(ptr,&y1); fprintf(rtf,"\\sb%d ",y1*scaling); } if(bits&0x0004) { ptr=scanint(ptr,&y1); fprintf(rtf,"\\sa%d ",y1*scaling); } if(bits&0x0008) { ptr=scanint(ptr,&y1); fprintf(rtf,"\\sl%d ",y1*scaling); } if(bits&0x0010) { ptr=scanint(ptr,&y1); fprintf(rtf,"\\li%d ",y1*scaling); } if(bits&0x0020) { ptr=scanint(ptr,&y1); fprintf(rtf,"\\ri%d ",y1*scaling); } if(bits&0x0040) { ptr=scanint(ptr,&y1); fprintf(rtf,"\\fi%d ",y1*scaling); } if(bits&0x0100) { x1=(unsigned char)*ptr++; if(x1&1) fprintf(rtf,"\\box "); if(x1&2) fprintf(rtf,"\\brdrt "); if(x1&4) fprintf(rtf,"\\brdrl "); if(x1&8) fprintf(rtf,"\\brdrb "); if(x1&0x10) fprintf(rtf,"\\brdrr "); if(x1&0x20) fprintf(rtf,"\\brdrth "); else fprintf(rtf,"\\brdrs "); if(x1&0x40) fprintf(rtf,"\\brdrdb "); ptr+=2; } if(bits&0x0200) { ptr=scanint(ptr,&y1); while(y1-->0) { ptr=scanword(ptr,&x1); if(x1&0x4000) { ptr=scanword(ptr,&x2); // tab switch(x2) { case 1: fprintf(rtf,"\\tqr"); break; case 2: fprintf(rtf,"\\tqc"); break; } } fprintf(rtf,"\\tx%d ",(x1&0x3FFF)*scaling); } } fprintf(rtf,"{"); while(1) // ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK)&&q<end) { if(*q) if(fontset!=-1&&(font[fontset].Attributes&FONT_SMCP)) strlwr(q); do { if(!doc) { BOOL first; while(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset<counter) { warnings=TRUE; if(warn) fprintf(stderr,"\ncounter %08lx behind %08lx\n",counter,ContextRec[NextContextRec].TopicOffset); fprintf(rtf,"{#{\\footnote # %s}}\n",unhash(ContextRec[NextContextRec].HashValue)); while(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset==ContextRec[NextContextRec+1].TopicOffset) { NextContextRec++; } NextContextRec++; } if(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset==counter) { fprintf(rtf,"{#{\\footnote # %s}}\n",unhash(ContextRec[NextContextRec].HashValue)); while(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset==ContextRec[NextContextRec+1].TopicOffset) { NextContextRec++; } NextContextRec++; } first=TRUE; while(NextKeywordRec<KeywordRecs&&KeywordRec[NextKeywordRec].TopicOffset<counter) { // warnings=TRUE; // if(warn) fprintf(stderr,"\ncount %08lx behind %08lx\n",counter,KeywordRec[NextKeywordRec].TopicOffset); if(first) { fprintf(rtf,"%c{\\footnote %c ",KeywordRec[NextKeywordRec].Footnote,KeywordRec[NextKeywordRec].Footnote); first=FALSE; } else { fprintf(rtf,"; "); } putrtf(rtf,KeywordRec[NextKeywordRec].Keyword); NextKeywordRec++; } if(!first) fprintf(rtf,"}\n"); first=TRUE; if(NextKeywordRec<KeywordRecs&&KeywordRec[NextKeywordRec].TopicOffset==counter) { if(first) { fprintf(rtf,"%c{\\footnote %c ",KeywordRec[NextKeywordRec].Footnote,KeywordRec[NextKeywordRec].Footnote); first=FALSE; } else { fprintf(rtf,"; "); } putrtf(rtf,KeywordRec[NextKeywordRec].Keyword); NextKeywordRec++; } if(!first) fprintf(rtf,"}\n"); } if(*q!='{'&&*q!='}'&&*q!='\\'&&isprint((unsigned char)*q)) { putc(*q,rtf); } else if(!doc&&*q=='{') { fprintf(rtf,"\\{\\-"); // emit invisible dash after { brace // because bmc or another legal command my follow, but this // command was not parsed the help file was build, so it was // used just as an example. The dash will be eaten up by the // help compiler on recompile. } else if(*q) { fprintf(rtf,"\\'%02x",(unsigned char)*q); } counter++; } while(*q++); if((unsigned char)ptr[0]==0xFF) { ptr++; break; } else switch((unsigned char)ptr[0]) { case 0x20: // vfld MVB if(*(long *)(ptr+1)) { fprintf(rtf,"\\{vfld%ld\\}",*(long *)(ptr+1)); } else { fprintf(rtf,"\\{vfld\\}"); } ptr+=5; break; case 0x21: // dtype MVB if(*(short *)(ptr+1)) { fprintf(rtf,"\\{dtype%d\\}",*(short *)(ptr+1)); } else { fprintf(rtf,"\\{dtype\\}"); } ptr+=3; break; case 0x80: // font change ChangeFont(rtf,fontset=*(short *)(ptr+1),ul,uldb); ptr+=3; break; case 0x81: fprintf(rtf,"\\line\n"); ptr++; break; case 0x82: if(TopicLink.RecordType==TL_TABLE) { if((unsigned char)ptr[1]!=0xFF) { fprintf(rtf,"\n\\par \\intbl "); } else if(*(short *)(ptr+2)==-1) { fprintf(rtf,"\\cell \\intbl \\row\n"); } else if(*(short *)(ptr+2)==lastcol) { fprintf(rtf,"\\par \\pard "); } else { fprintf(rtf,"\\cell \\pard "); } } else { fprintf(rtf,"\n\\par "); } ptr++; break; case 0x83: fprintf(rtf,"\\tab "); ptr++; break; case 0x86: x3=(unsigned char)*ptr++; x1=*ptr++; if(x1==0x05) cmd="ewc"; else cmd="bmc"; goto picture; case 0x87: x3=(unsigned char)*ptr++; x1=*ptr++; if(x1==0x05) cmd="ewl"; else cmd="bml"; goto picture; case 0x88: x3=(unsigned char)*ptr++; x1=*ptr++; if(x1==0x05) cmd="ewr"; else cmd="bmr"; goto picture; picture: ptr=scanlong(ptr,&l1); switch(x1) { case 0x22: // HC31 ptr=scanword(ptr,&x1); counter+=x1; // number of hotspots in picture added // because value of counter is used inside WinHelp // to record actual position (selected hotspot). // fall thru case 0x03: // HC30 x1=((unsigned short *)ptr)[0]; switch(x1) { case 1: while(nextbitmap<extensions&&extension[nextbitmap]<0x10) nextbitmap++; if(nextbitmap>=extensions) { error("Bitmap never saved\n"); break; } x2=nextbitmap++; goto other; case 0: x2=((unsigned short *)ptr)[1]; other: if(doc) { switch(x3) { case 0x86: fprintf(rtf,"{\\field {\\*\\fldinst import %s \\* Mergeformat}}",getbitmapname(x2)); break; case 0x87: fprintf(rtf,"{\\pvpara {\\field {\\*\\fldinst import %s \\* Mergeformat}}\\par}\n",getbitmapname(x2)); break; case 0x88: fprintf(rtf,"{\\pvpara\\posxr{\\field {\\*\\fldinst import %s \\* Mergeformat}}\\par}\n",getbitmapname(x2)); break; } } else { if(x2<extensions&&(extension[x2]&0x0F)==5) { if(strcmp(cmd,"bmc")==0) cmd="bmct"; else if(strcmp(cmd,"bml")==0) cmd="bmlt"; else if(strcmp(cmd,"bmr")==0) cmd="bmrt"; } fprintf(rtf,"\\{%s %s\\}",cmd,getbitmapname(x2)); } break; } break; case 0x05: // ewc,ewl,ewr if(ptr[6]=='!') { fprintf(rtf,"\\{button %s\\}",ptr+7); } else if(ptr[6]=='*') { char *plus; int n,c1,c2; sscanf(ptr+7,"%d,%d,%n",&c1,&c2,&n); plus=strchr(ptr+7+n,'+'); if((c1&0xFFF5)!=0x8400) fprintf(stderr,"mci c1=%04x\n",c1); fprintf(rtf,"\\{mci"); if(cmd[2]=='r') fprintf(rtf,"_right"); if(cmd[2]=='l') fprintf(rtf,"_left"); if(c2==1) fprintf(rtf," REPEAT"); if(c2==2) fprintf(rtf," PLAY"); if(!plus) fprintf(rtf," EXTERNAL"); if(c1&8) fprintf(rtf," NOMENU"); if(c1&2) fprintf(rtf," NOPLAYBAR"); fprintf(rtf,",%s\\}\n",plus?plus+1:ptr+7+n); } else { fprintf(rtf,"\\{%s %s\\}",cmd,ptr+6); } break; default: error("Unknown picture flags %02x\n",x1); } ptr+=l1; break; case 0x89: // end of hotspot ChangeFont(rtf,fontset,ul=FALSE,uldb=FALSE); fprintf(rtf,"{\\v %s}",hotspot); ptr++; break; case 0xC8: // macro ChangeFont(rtf,fontset,FALSE,uldb=TRUE); hotspot=myReAlloc(hotspot,strlen(ptr+3)+2); sprintf(hotspot,"!%s",ptr+3); ptr+=*(short *)(ptr+1)+3; break; case 0xCC: // macro without font change ChangeFont(rtf,fontset,FALSE,uldb=TRUE); hotspot=myReAlloc(hotspot,strlen(ptr+3)+3); sprintf(hotspot,"*!%s",ptr+3); ptr+=*(short *)(ptr+1)+3; break; case 0xE0: // popup jump HC30 ChangeFont(rtf,fontset,ul=TRUE,FALSE); goto label0; case 0xE1: // topic jump HC30 ChangeFont(rtf,fontset,FALSE,uldb=TRUE); label0: hotspot=myReAlloc(hotspot,128); sprintf(hotspot,"TOPIC%ld",*(long *)(ptr+1)); ptr+=5; break; case 0xE2: // popup jump HC31 ChangeFont(rtf,fontset,ul=TRUE,FALSE); goto label1; case 0xE3: // topic jump HC31 ChangeFont(rtf,fontset,FALSE,uldb=TRUE); label1: arg=unhash(*(long *)(ptr+1)); hotspot=myReAlloc(hotspot,strlen(arg)+1); sprintf(hotspot,"%s",arg); ptr+=5; break; case 0xE6: // popup jump without font change ChangeFont(rtf,fontset,ul=TRUE,FALSE); goto label2; case 0xE7: // topic jump without font change ChangeFont(rtf,fontset,FALSE,uldb=TRUE); label2: arg=unhash(*(long *)(ptr+1)); hotspot=myReAlloc(hotspot,strlen(arg)+2); sprintf(hotspot,"*%s",arg); ptr+=5; break; case 0xEA: // popup jump into external file case 0xEE: ChangeFont(rtf,fontset,ul=TRUE,FALSE); goto label3; case 0xEB: // topic jump into external file / secondary window case 0xEF: ChangeFont(rtf,fontset,FALSE,uldb=TRUE); label3: if((unsigned char)ptr[0]==0xEE||(unsigned char)ptr[0]==0xEF) { cmd="*"; } else { cmd=""; } arg=unhash(*(long *)(ptr+4)); switch((unsigned char)ptr[3]) { case 0: hotspot=myReAlloc(hotspot,strlen(cmd)+strlen(arg)+1); sprintf(hotspot,"%s%s",cmd,arg); break; case 1: hotspot=myReAlloc(hotspot,strlen(cmd)+strlen(arg)+1+strlen(WindowName(ptr[8]))+1); sprintf(hotspot,"%s%s>%s",cmd,arg,WindowName(ptr[8])); break; case 4: hotspot=myReAlloc(hotspot,strlen(cmd)+strlen(arg)+1+strlen(ptr+8)+1); sprintf(hotspot,"%s%s@%s",cmd,arg,ptr+8); break; case 6: hotspot=myReAlloc(hotspot,strlen(cmd)+strlen(arg)+1+strlen(ptr+8)+1+strlen(strchr(ptr+8,'\0')+1)+1); sprintf(hotspot,"%s%s>%s@%s",cmd,arg,ptr+8,strchr(ptr+8,'\0')+1); break; default: error("Unknown modifier %02x in tag %02x\n",(unsigned char)ptr[3],(unsigned char)ptr[0]); } ptr+=*(short *)(ptr+1)+3; break; case 0x8B: // unknown, found in BACKSDK.MVB case 0x8C: // unknown, found in BACKSDK.MVB default: fprintf(stderr,"\n%02x unknown\n",(unsigned char)ptr[0]); ptr++; } } fprintf(rtf,"}"); } } else { if(LinkData1) { printf("LinkData1:"); for(i=0;i<TopicLink.DataLen1-sizeof(TOPICLINK);i++) { if(i%16==0) printf("\n%04x",i); printf(" %02X",(unsigned char)LinkData1[i]); } if(i%16) printf("\n"); } if(LinkData2) { printf("LinkData2:"); for(i=0;i<TopicLink.DataLen2;i++) { if(i%16==0) printf("\n%04x",i); printf(" %02X",(unsigned char)LinkData2[i]); } if(i%16) printf("\n"); } } if(LinkData1) free(LinkData1); if(LinkData2) free(LinkData2); dontCount=FALSE; memcpy(&TopicLink,&NextTopicLink,sizeof(TopicLink)); } } } void TTLDump(FILE *HelpFile,long FileStart) { char Title[256]; int entries,count; long TopicOffset; BUFFER buf; entries=GetFirstPage(HelpFile,FileStart,&buf); while(entries) { for(count=1;count<=entries;count++) { myFRead(&TopicOffset,sizeof(TopicOffset),HelpFile); StringRead(Title,sizeof(Title),HelpFile); printf("Topic Offset:0x%08lX Title: %s\n",TopicOffset,Title); } entries=GetNextPage(HelpFile,&buf); } } int ContextRecCmp(const void *a,const void *b) { const CONTEXTREC *A; const CONTEXTREC *B; A=(const CONTEXTREC *)a; B=(const CONTEXTREC *)b; if(A->TopicOffset<B->TopicOffset) return -1; if(A->TopicOffset>B->TopicOffset) return 1; return 0; } void ContextLoad(FILE *HelpFile) { long FileStart; BUFFER buf; int entries; if(SearchFile(HelpFile,"|CONTEXT",&FileStart)) { entries=GetFirstPage(HelpFile,FileStart,&buf); while(entries) { ContextRec=myReAlloc(ContextRec,(long)(ContextRecs+entries)*sizeof(CONTEXTREC)); myFRead(ContextRec+ContextRecs,entries*sizeof(CONTEXTREC),HelpFile); ContextRecs+=entries; entries=GetNextPage(HelpFile,&buf); } qsort(ContextRec,ContextRecs,sizeof(CONTEXTREC),ContextRecCmp); printf("%d topic offsets and hash values loaded\n",ContextRecs); } } void GenerateContent(FILE *HelpFile,FILE *ContentFile) { typedef struct { long TopicOffset; long WindowNumber; } WINDOWREC; WINDOWREC *WindowRec; int entries,count,WindowRecs; BUFFER buf; long FileStart,CurrentLocation,lastpos,TopicOffset; FILEHEADER FileHdr; SYSTEMREC SystemRec; SECWINDOW *SWin; int i,windows; char *data; fprintf(ContentFile,":Base %s%s>main\n",name,ext); if(SearchFile(HelpFile,"|SYSTEM",&FileStart)) { fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); myFRead(&SysHeader,sizeof(SysHeader),HelpFile); if(before31) { myFRead(buffer,FileHdr.FileSize-sizeof(SysHeader),HelpFile); fprintf(ContentFile,":Title %s\n",buffer); } else { CurrentLocation=12; lastpos=ftell(HelpFile); SWin=NULL; windows=0; while(CurrentLocation<FileHdr.FileSize) { myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile); data=myMalloc(SystemRec.DataSize+1); myFRead(data,SystemRec.DataSize,HelpFile); CurrentLocation=CurrentLocation+4+SystemRec.DataSize; data[SystemRec.DataSize]='\0'; switch(SystemRec.RecordType) { case 0x0001: strcpy(buffer,data); fprintf(ContentFile,":Title %s\n",buffer); break; case 0x0004: AddMacro(data); break; case 0x0006: windows++; break; } free(data); } if(windows) { secondarywindownames=myMalloc((windows+1)*sizeof(char *)); for(i=0;i<=windows;i++) secondarywindownames[i]=NULL; fseek(HelpFile,lastpos,SEEK_SET); CurrentLocation=12; i=0; while(CurrentLocation<FileHdr.FileSize) { myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile); data=myMalloc(SystemRec.DataSize+1); myFRead(data,SystemRec.DataSize,HelpFile); data[SystemRec.DataSize]='\0'; CurrentLocation=CurrentLocation+4+SystemRec.DataSize; switch(SystemRec.RecordType) { case 0x0006: SWin=(SECWINDOW *)data; if(SWin->Flags&WSYSFLAG_NAME) { secondarywindownames[i]=myStrDup(SWin->Name); } i++; break; } free(data); } } } } ContextLoad(HelpFile); if(SearchFile(HelpFile,"|TopicId",&FileStart)) { int entries,count; long offset; BUFFER buf; entries=GetFirstPage(HelpFile,FileStart,&buf); while(entries) { for(count=1;count<=entries;count++) { myFRead(&offset,sizeof(offset),HelpFile); StringRead(buffer,sizeof(buffer),HelpFile); AddTopic(buffer); } entries=GetNextPage(HelpFile,&buf); } } WindowRec=NULL; WindowRecs=0; if(SearchFile(HelpFile,"|Petra",&FileStart)) { entries=GetFirstPage(HelpFile,FileStart,&buf); while(entries) { WindowRec=myReAlloc(WindowRec,(long)(WindowRecs+entries)*sizeof(WINDOWREC)); myFRead(WindowRec+WindowRecs,entries*sizeof(WINDOWREC),HelpFile); WindowRecs+=entries; entries=GetNextPage(HelpFile,&buf); } } if(SearchFile(HelpFile,"|TTLBTREE",&FileStart)) { entries=GetFirstPage(HelpFile,FileStart,&buf); while(entries) { for(count=1;count<=entries;count++) { myFRead(&TopicOffset,sizeof(TopicOffset),HelpFile); if(StringRead(buffer,sizeof(buffer),HelpFile)) { char *ptr; ptr=TopicName(TopicOffset,FALSE); if(ptr) { fprintf(ContentFile,"1 %s=%s",buffer,ptr); for(i=0;i<WindowRecs;i++) { if(WindowRec[i].TopicOffset==TopicOffset) { fprintf(ContentFile,">%s",WindowName(WindowRec[i].WindowNumber)); break; } } fprintf(ContentFile,"\n"); } else { fprintf(stderr,"%s\n",buffer); } } } entries=GetNextPage(HelpFile,&buf); } } } void RoseDump(FILE *HelpFile,long FileStart) { char buffer[1024]; int entries,count,len; long offset; BUFFER buf; entries=GetFirstPage(HelpFile,FileStart,&buf); while(entries) { for(count=1;count<=entries;count++) { myFRead(&offset,sizeof(offset),HelpFile); len=StringRead(buffer,sizeof(buffer),HelpFile); StringRead(buffer+len+1,sizeof(buffer)-len-1,HelpFile); printf("0x%08lX: %s,%s\n",offset,buffer,buffer+len+1); } entries=GetNextPage(HelpFile,&buf); } } void TopicIdDump(FILE *HelpFile,long FileStart) { char buffer[1024]; int entries,count; long offset; BUFFER buf; entries=GetFirstPage(HelpFile,FileStart,&buf); while(entries) { for(count=1;count<=entries;count++) { myFRead(&offset,sizeof(offset),HelpFile); StringRead(buffer,sizeof(buffer),HelpFile); printf("0x%08lX: %s\n",offset,buffer); } entries=GetNextPage(HelpFile,&buf); } } void PetraDump(FILE *HelpFile,long FileStart) { char buffer[1024]; int entries,count; long offset; BUFFER buf; entries=GetFirstPage(HelpFile,FileStart,&buf); while(entries) { for(count=1;count<=entries;count++) { myFRead(&offset,sizeof(offset),HelpFile); StringRead(buffer,sizeof(buffer),HelpFile); printf("0x%08lX: %s\n",offset,buffer); } entries=GetNextPage(HelpFile,&buf); } } void VIOLADump(FILE *HelpFile,long FileStart) { int entries,count; long offset,value; BUFFER buf; long SystemStart,CurrentLocation,lastpos; FILEHEADER FileHdr; SYSTEMREC SystemRec; SECWINDOW *SWin; int i,windows; char *data; if(SearchFile(HelpFile,"|SYSTEM",&SystemStart)) { fseek(HelpFile,SystemStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); myFRead(&SysHeader,sizeof(SysHeader),HelpFile); if(!before31) { CurrentLocation=12; lastpos=ftell(HelpFile); SWin=NULL; windows=0; while(CurrentLocation<FileHdr.FileSize) { myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile); CurrentLocation+=4; if(SystemRec.DataSize) { data=myMalloc(SystemRec.DataSize+1); myFRead(data,SystemRec.DataSize,HelpFile); data[SystemRec.DataSize]='\0'; switch(SystemRec.RecordType) { case 0x0006: windows++; } free(data); } } if(windows) { secondarywindownames=myMalloc((windows+1)*sizeof(char *)); for(i=0;i<=windows;i++) secondarywindownames[i]=NULL; fseek(HelpFile,lastpos,SEEK_SET); CurrentLocation=12; i=0; while(CurrentLocation<FileHdr.FileSize) { myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile); data=myMalloc(SystemRec.DataSize+1); myFRead(data,SystemRec.DataSize,HelpFile); data[SystemRec.DataSize]='\0'; CurrentLocation=CurrentLocation+4+SystemRec.DataSize; switch(SystemRec.RecordType) { case 0x0006: SWin=(SECWINDOW *)data; if(SWin->Flags&WSYSFLAG_NAME) { secondarywindownames[i]=myStrDup(SWin->Name); } i++; break; } free(data); } } } } entries=GetFirstPage(HelpFile,FileStart,&buf); while(entries) { for(count=1;count<=entries;count++) { myFRead(&offset,sizeof(offset),HelpFile); myFRead(&value,sizeof(value),HelpFile); printf("Topic Offset: 0x%08lX Window: %s\n",offset,WindowName(value)); } entries=GetNextPage(HelpFile,&buf); } } void KWMapDump(FILE *HelpFile,long FileStart) { FILEHEADER FileHdr; unsigned int NumKWMaps,count; KWMAPREC KeywordMap; fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); myFRead(&NumKWMaps,sizeof(NumKWMaps),HelpFile); for(count=1;count<=NumKWMaps;count++) { myFRead(&KeywordMap,sizeof(KWMAPREC),HelpFile); printf("Record:%05u First Keyword:0x%08lX Leaf Page#:%05u\n",count,KeywordMap.FirstRec,KeywordMap.PageNum); } } void KWDataDump(FILE *HelpFile,long FileStart) { FILEHEADER FileHdr; unsigned short NumKWLocs,count; long TopicOffset; fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); NumKWLocs=(unsigned short)(FileHdr.FileSize/4); printf("Number of Keyword Occurrances: %5u\n\n",NumKWLocs); for(count=1;count<=NumKWLocs;count++) { myFRead(&TopicOffset,sizeof(TopicOffset),HelpFile); printf("Occurance:%05u Topic Offset:0x%08lX\n",count,TopicOffset); } } void KWBTreeDump(FILE *HelpFile,long FileStart) { int entries,i; char Keyword[256]; // Variable Length Keyword short Count; // Count of Keywords occurances long KWDataOffset; // Offset into |KWDATA file BUFFER buf; entries=GetFirstPage(HelpFile,FileStart,&buf); while(entries) { for(i=1;i<=entries;i++) { StringRead(Keyword,sizeof(Keyword),HelpFile); myFRead(&Count,sizeof(Count),HelpFile); myFRead(&KWDataOffset,sizeof(KWDataOffset),HelpFile); printf("KWData Offset:0x%08lX # Offsets:%05d Keyword: %s\n",KWDataOffset,Count,Keyword); } entries=GetNextPage(HelpFile,&buf); } } void CatalogDump(FILE *HelpFile,long FileStart) { FILEHEADER FileHdr; struct { unsigned short magic; // 0x1111 unsigned short always8; unsigned short always4; long entries; unsigned char zero[30]; } catalog; long count; long TopicOffset; int i; fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); myFRead(&catalog,sizeof(catalog),HelpFile); printf("|CATALOG Dump: 0x%04x 0x%04x 0x%04x %ld\n",catalog.magic,catalog.always8,catalog.always4,catalog.entries); for(i=0;i<16;i++) printf("%02x ",catalog.zero[i]); printf("\n"); for(i=16;i<30;i++) printf("%02x ",catalog.zero[i]); printf("\nTopic Offsets:\n"); for(count=0;count<catalog.entries;count++) { myFRead(&TopicOffset,sizeof(TopicOffset),HelpFile); printf("%08lx %c",TopicOffset,count%8==7?'\n':' '); } if(count%8) printf("\n"); } void ContextDump(FILE *HelpFile,long FileStart) { int i,entries; CONTEXTREC ContextRec; BUFFER buf; entries=GetFirstPage(HelpFile,FileStart,&buf); while(entries) { for(i=0;i<entries;i++) { myFRead(&ContextRec,sizeof(ContextRec),HelpFile); printf("hash value: 0x%08lx string: %-20s topic offset: 0x%08lX\n",ContextRec.HashValue,unhash(ContextRec.HashValue),ContextRec.TopicOffset); } entries=GetNextPage(HelpFile,&buf); } } void AliasList(FILE *hpj) { int i,n; BOOL headerwritten; if(ContextRec) { headerwritten=FALSE; for(i=0;i<ContextRecs;i=n) { for(n=i+1;n<ContextRecs&&ContextRec[i].TopicOffset==ContextRec[n].TopicOffset;n++) { if(!headerwritten) { fprintf(stderr,"Creating [ALIAS] section...\n"); fprintf(hpj,"[ALIAS]\n"); headerwritten=TRUE; } fprintf(hpj,"%s=",unhash(ContextRec[n].HashValue)); fprintf(hpj,"%s\n",unhash(ContextRec[i].HashValue)); } } if(headerwritten) fprintf(hpj,"\n"); } } void CTXOMAPDump(FILE *HelpFile,long FileStart) { FILEHEADER FileHdr; CTXOMAPREC CTXORec; unsigned short NumRecs,i; fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); myFRead(&NumRecs,sizeof(NumRecs),HelpFile); printf("Number of Context Mapping records: %u\n\n",NumRecs); for(i=1;i<=NumRecs;i++) { myFRead(&CTXORec,sizeof(CTXORec),HelpFile); printf("record:%05u map ID:0x%08lX topic offset:0x%08lX\n",i,CTXORec.MapID,CTXORec.TopicOffset); } } void CTXOMAPList(FILE *HelpFile,FILE *hpj) { FILEHEADER FileHdr; CTXOMAPREC CTXORec; unsigned short NumRecs,i; long FileStart; if(hpj&&SearchFile(HelpFile,"|CTXOMAP",&FileStart)) { fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); myFRead(&NumRecs,sizeof(NumRecs),HelpFile); if(NumRecs) { fprintf(stderr,"Creating [MAP] section...\n"); fprintf(hpj,"[MAP]\n"); for(i=0;i<NumRecs;i++) { myFRead(&CTXORec,sizeof(CTXORec),HelpFile); fprintf(hpj,"%s %ld\n",TopicName(CTXORec.TopicOffset,TRUE),CTXORec.MapID); } fprintf(hpj,"\n"); } } } // 1. extract topic names from topic macros, embedded pictures, and hotspot macros // 2. build browse sequence start list // 3. extract embedded pictures void FirstPass(FILE *HelpFile) { long FileStart; long TopicNum; int BytesRead; FILEHEADER FileHdr; if(extractmacros) { if(!before31&&SearchFile(HelpFile,"|SYSTEM",&FileStart)) { SYSTEMREC SystemRec; long CurrentLocation; char *data; fseek(HelpFile,FileStart,SEEK_SET); myFRead(&FileHdr,sizeof(FileHdr),HelpFile); myFRead(&SysHeader,sizeof(SysHeader),HelpFile); CurrentLocation=12; while(CurrentLocation<FileHdr.FileSize) { myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile); CurrentLocation+=4; if(SystemRec.DataSize) { data=myMalloc(SystemRec.DataSize+1); myFRead(data,SystemRec.DataSize,HelpFile); data[SystemRec.DataSize]='\0'; CurrentLocation+=SystemRec.DataSize; switch(SystemRec.RecordType) { case 0x0004: if(checkexternal) { CheckMacro(data); } else { AddMacro(data); } break; } free(data); } } } if(!checkexternal&&!before31&&SearchFile(HelpFile,"|TopicId",&FileStart)) { char buffer[1024]; int entries,count; long offset; BUFFER buf; entries=GetFirstPage(HelpFile,FileStart,&buf); while(entries) { for(count=1;count<=entries;count++) { myFRead(&offset,sizeof(offset),HelpFile); StringRead(buffer,sizeof(buffer),HelpFile); AddTopic(buffer); } entries=GetNextPage(HelpFile,&buf); } } } TopicNum=16; TopicUse=TRUE; // extract macros from topic headers browses=0; browsenums=1; if(SearchFile(HelpFile,"|TOPIC",&FileStart)) { TOPICLINK TopicLink; TOPICLINK NextTopicLink; char *LinkData1; char *LinkData2; long offset; fseek(HelpFile,FileStart,SEEK_SET); dontCount=FALSE; BytesRead=(int)TopicRead(HelpFile,12L,&TopicLink,sizeof(TopicLink)); while(BytesRead==sizeof(TOPICLINK)) { offset=TopicOffset; if(TopicLink.DataLen1>sizeof(TOPICLINK)) { LinkData1=myMalloc(TopicLink.DataLen1-sizeof(TOPICLINK)+1); if(!LinkData1) break; if(!TopicRead(HelpFile,0L,LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK))) break; } else LinkData1=NULL; if(TopicLink.DataLen1<TopicLink.BlockSize) // read LinkData2 without phrase replacement { LinkData2=myMalloc(TopicLink.BlockSize-TopicLink.DataLen1+1); if(!LinkData2) break; if(!TopicRead(HelpFile,0L,LinkData2,TopicLink.BlockSize-TopicLink.DataLen1)) break; } else LinkData2=NULL; BytesRead=(int)TopicRead(HelpFile,0L,&NextTopicLink,sizeof(NextTopicLink)); if(TopicLink.RecordType==TL_TOPICHDR) // display a topic header record { fprintf(stderr,"."); if(before31) { TOPICHEADER30 *TopicHdr; TopicHdr=(TOPICHEADER30 *)LinkData1; if(resolvebrowse) { if(TopicHdr->NextTopicNum>TopicNum&&TopicHdr->PrevTopicNum>TopicNum || TopicHdr->NextTopicNum==-1&&TopicHdr->PrevTopicNum>TopicNum || TopicHdr->NextTopicNum>TopicNum&&TopicHdr->PrevTopicNum==-1) { AddBrowse(TopicNum,TopicHdr->NextTopicNum,TopicHdr->PrevTopicNum); } else if(TopicHdr->NextTopicNum!=-1&&TopicHdr->NextTopicNum<TopicNum&&TopicHdr->PrevTopicNum!=-1&&TopicHdr->PrevTopicNum<TopicNum) { MergeBrowse(TopicNum,TopicHdr->NextTopicNum,TopicHdr->PrevTopicNum); } else if(TopicHdr->NextTopicNum!=-1&&TopicHdr->NextTopicNum<TopicNum&&(TopicHdr->PrevTopicNum==-1||TopicHdr->PrevTopicNum>TopicNum)) { BackLinkBrowse(TopicNum,TopicHdr->NextTopicNum,TopicHdr->PrevTopicNum); } else if(TopicHdr->PrevTopicNum!=-1&&TopicHdr->PrevTopicNum<TopicNum&&(TopicHdr->NextTopicNum==-1||TopicHdr->NextTopicNum>TopicNum)) { LinkBrowse(TopicNum,TopicHdr->NextTopicNum,TopicHdr->PrevTopicNum); } } TopicNum++; } else { TOPICHEADER *TopicHdr; TopicHdr=(TOPICHEADER *)LinkData1; if(resolvebrowse) { if(TopicHdr->BrowseFor>offset&&TopicHdr->BrowseBck>offset || TopicHdr->BrowseFor==-1L&&TopicHdr->BrowseBck>offset || TopicHdr->BrowseFor>offset&&TopicHdr->BrowseBck==-1L) { AddBrowse(offset,TopicHdr->BrowseFor,TopicHdr->BrowseBck); } else if(TopicHdr->BrowseFor!=-1L&&TopicHdr->BrowseFor<offset&&TopicHdr->BrowseBck!=-1L&&TopicHdr->BrowseBck<offset) { MergeBrowse(offset,TopicHdr->BrowseFor,TopicHdr->BrowseBck); } else if(TopicHdr->BrowseFor!=-1L&&TopicHdr->BrowseFor<offset&&(TopicHdr->BrowseBck==-1L||TopicHdr->BrowseBck>offset)) { BackLinkBrowse(offset,TopicHdr->BrowseFor,TopicHdr->BrowseBck); } else if(TopicHdr->BrowseBck!=-1L&&TopicHdr->BrowseBck<offset&&(TopicHdr->BrowseFor==-1L||TopicHdr->BrowseFor>offset)) { LinkBrowse(offset,TopicHdr->BrowseFor,TopicHdr->BrowseBck); } } if(extractmacros&&TopicLink.DataLen2>0) { int i,n; char *end; if(TopicLink.DataLen2<=TopicLink.BlockSize-TopicLink.DataLen1) { end=LinkData2+TopicLink.DataLen2; } else { char *q; q=myMalloc(TopicLink.DataLen2+1); end=StringPrint(LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,q); if(end>q+TopicLink.DataLen2) { error("Phrase replacement delivers %u instead of %ld bytes\n",(unsigned int)(end-q),TopicLink.DataLen2); } free(LinkData2); LinkData2=q; } *end='\0'; for(i=strlen(LinkData2)+1;i<TopicLink.DataLen2;i+=n+1) { n=strlen(LinkData2+i); // because AddMacro destroys string if(checkexternal) { CheckMacro(LinkData2+i); } else { AddMacro(LinkData2+i); } } } } } else if(TopicLink.RecordType==TL_DISPLAY30||TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE) { int col,cols; char filename[20]; unsigned short x1,x2,x3,x4; short y1; long l1; char *ptr; unsigned short bits; ptr=scanlong(LinkData1,&l1); switch(TopicLink.RecordType) { case TL_DISPLAY: ptr=scanword(ptr,&x3); if(!dontCount) TopicOffset+=x3; break; case TL_TABLE: ptr=scanword(ptr,&x3); if(!dontCount) TopicOffset+=x3; cols=(unsigned char)*ptr++; x4=(unsigned char)*ptr++; switch(x4) { case 0: // found in CALC.HLP and TERMINAL.HLP case 2: ptr+=2; case 1: case 3: break; default: error("\nunknown column data modifier %02x found\n",x4); } ptr+=4*cols; break; } for(col=0;(TopicLink.RecordType==TL_TABLE?*(short *)ptr!=-1:col==0)&&ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK);col++) { if(TopicLink.RecordType==TL_TABLE) ptr+=5; ptr+=4; bits=*(unsigned short *)ptr; ptr+=2; if(bits&0x0001) ptr=scanlong(ptr,&l1); // found in MSDNCD9.MVB, purpose unknown if(bits&0x0002) ptr=scanint(ptr,&y1); if(bits&0x0004) ptr=scanint(ptr,&y1); if(bits&0x0008) ptr=scanint(ptr,&y1); if(bits&0x0010) ptr=scanint(ptr,&y1); if(bits&0x0020) ptr=scanint(ptr,&y1); if(bits&0x0040) ptr=scanint(ptr,&y1); if(bits&0x0100) ptr+=3; if(bits&0x0200) { ptr=scanint(ptr,&y1); while(y1-->0) { ptr=scanword(ptr,&x1); if(x1&0x4000) ptr=scanword(ptr,&x2); } } while(ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK)) { if((unsigned char)ptr[0]==0xFF) { ptr++; break; } else switch((unsigned char)ptr[0]) { case 0x21: // dtype (MVB) case 0x80: // font change ptr+=3; break; case 0x81: case 0x82: case 0x83: case 0x89: // end of hotspot ptr++; break; case 0x86: case 0x87: case 0x88: ptr++; x1=*ptr++; ptr=scanlong(ptr,&l1); switch(x1) { case 0x22: // HC31 ptr=scanword(ptr,&x1); // fall thru case 0x03: // HC30 switch(((unsigned short *)ptr)[0]) { case 1: if(checkexternal) { CheckBitmap(CreateMap(ptr+2,l1-2)); } else { for(x2=1;x2<extensions;x2++) if(!extension[x2]) break; if(x2>=extensions) { extension=myReAlloc(extension,(x2+1)*sizeof(char)); while(extensions<=x2) extension[extensions++]=0; } sprintf(filename,"bm%u",x2); x1=ExtractBitmap(filename,CreateMap(ptr+2,l1-2),l1-2); extension[x2]=x1|0x10; } break; } break; case 0x05: if(ptr[6]=='!'&&strchr(ptr+7,',')) { if(checkexternal) { CheckMacro(strchr(ptr+7,',')+1); } else if(extractmacros) { AddMacro(strchr(ptr+7,',')+1); } } break; } ptr+=l1; break; case 0xC8: // macro case 0xCC: // macro without font change if(checkexternal) { CheckMacro(ptr+3); } else if(extractmacros) { AddMacro(ptr+3); } ptr+=*(short *)(ptr+1)+3; break; case 0x20: // vfld (MVC) case 0xE0: // popup jump HC30 case 0xE1: // topic jump HC30 case 0xE2: // popup jump HC31 case 0xE3: // jump jump HC31 case 0xE6: // popup jump without font change case 0xE7: // jump jump without font change ptr+=5; break; case 0xEA: // popup jump into external file case 0xEB: // topic jump into external file / secondary window case 0xEE: // popup jump into external file without font change case 0xEF: // topic jump into external file / secondary window without font change if(checkexternal) { switch((unsigned char)ptr[3]) { case 0: case 1: break; case 4: if(!CheckHash(ptr+8,*(long *)(ptr+4))) { fprintf(stderr,"0x%08lx@%s not satisfied\n",*(long *)(ptr+4),ptr+8); } break; case 6: if(!CheckHash(ptr+8,*(long *)(ptr+4))) { fprintf(stderr,"0x%08lx>%s@%s not satisfied\n",*(long *)(ptr+4),ptr+8,strchr(ptr+8,'\0')+1); } break; default: error("Unknown modifier %02x in tag %02x\n",(unsigned char)ptr[3],(unsigned char)ptr[0]); } } ptr+=*(short *)(ptr+1)+3; break; case 0x8B: // unknown, found in BACKSDK.MVB case 0x8C: // unknown, found in BACKSDK.MVB default: fprintf(stderr,"\n%02x unknown",(unsigned char)ptr[0]); ptr++; } } } } if(LinkData1) free(LinkData1); if(LinkData2) free(LinkData2); dontCount=FALSE; memcpy(&TopicLink,&NextTopicLink,sizeof(TopicLink)); } } TopicFileStart=0L; } void DumpTopic(FILE *HelpFile,long FileStart) { TOPICLINK TopicLink; TOPICLINK NextTopicLink; char *LinkData1; char *LinkData2; int BytesRead; fseek(HelpFile,FileStart,SEEK_SET); BytesRead=(int)TopicRead(HelpFile,12L,&TopicLink,sizeof(TopicLink)); while(BytesRead==sizeof(TOPICLINK)) { printf("-------------------------------------------------------------------------\n"); printf("TopicLink: BlockSize=%08lx DataLen2=%08lx PrevBlock=%08lx\nNextBlock=%08lx DataLen1=%08lx Type=%02x\n",TopicLink.BlockSize,TopicLink.DataLen2,TopicLink.PrevBlock,TopicLink.NextBlock,TopicLink.DataLen1,TopicLink.RecordType); if(TopicLink.DataLen1>sizeof(TOPICLINK)) { LinkData1=myMalloc(TopicLink.DataLen1-sizeof(TOPICLINK)); TopicRead(HelpFile,0L,LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK)); } else LinkData1=NULL; if(TopicLink.DataLen1<TopicLink.BlockSize) // read LinkData2 without phrase replacement { LinkData2=myMalloc(TopicLink.BlockSize-TopicLink.DataLen1); TopicRead(HelpFile,0L,LinkData2,TopicLink.BlockSize-TopicLink.DataLen1); } else LinkData2=NULL; BytesRead=(int)TopicRead(HelpFile,0L,&NextTopicLink,sizeof(NextTopicLink)); if(LinkData1) { HexDumpMemory(LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK)); } if(TopicLink.RecordType==TL_TOPICHDR&&before31) { TOPICHEADER30 *TopicHdr; TopicHdr=(TOPICHEADER30 *)LinkData1; printf("BlockSize=%ld PrevTopicNum=%ld NextTopicNum=%ld\n",TopicHdr->BlockSize,TopicHdr->PrevTopicNum,TopicHdr->NextTopicNum); } else if(TopicLink.RecordType==TL_TOPICHDR&&!before31) { TOPICHEADER *TopicHdr; TopicHdr=(TOPICHEADER *)LinkData1; printf("BlockSize=%ld BrowseBck=%08lx BrowseFor=%08lx TopicNum=%ld\n",TopicHdr->BlockSize,TopicHdr->BrowseBck,TopicHdr->BrowseFor,TopicHdr->TopicNum); printf("NonScroll=%08lx Scroll=%08lx NextTopic=%08lx\n",TopicHdr->NonScroll,TopicHdr->Scroll,TopicHdr->NextTopic); } else if(TopicLink.RecordType==TL_DISPLAY30||TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE) { char *ptr; char *str; char *cmd; long l; unsigned short w,bits; unsigned char b,cols,col; short i; ptr=scanlong(LinkData1,&l); printf("expandedsize=%ld ",l); switch(TopicLink.RecordType) { case TL_DISPLAY: ptr=scanword(ptr,&w); printf("topicoffsetincrement=%u ",w); break; case TL_TABLE: ptr=scanword(ptr,&w); printf("topicoffsetincrement=%u ",w); cols=*ptr++; printf("columns=%d ",cols); b=*ptr++; printf("type=%d ",b); switch(b) { case 0: case 2: printf("minwidth=%d ",*(short *)ptr); ptr+=2; case 1: case 3: break; default: printf("unknown "); } for(i=0;i<cols;i++) { printf("width=%d ",((short *)ptr)[0]); printf("gap=%d ",((short *)ptr)[1]); ptr+=4; } break; } printf("\n"); if(TopicLink.DataLen2<=TopicLink.BlockSize-TopicLink.DataLen1) { str=LinkData2; } else { str=myMalloc(TopicLink.DataLen2+1); StringPrint(LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,str); free(LinkData2); LinkData2=str; } for(col=0;(TopicLink.RecordType==TL_TABLE?*(short *)ptr!=-1:col==0)&&ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK);col++) { if(TopicLink.RecordType==TL_TABLE) { printf("column=%d ",*(short *)ptr); ptr+=2; printf("%04x ",*(unsigned short *)ptr); ptr+=2; printf("%d ",(unsigned char)*ptr++-0x80); } printf("%02x ",*ptr++); printf("%d ",(unsigned char)*ptr++-0x80); printf("id=%04x ",*(unsigned short *)ptr); ptr+=2; bits=*(unsigned short *)ptr; ptr+=2; if(bits&0x0001) // found in MSDNCD9.MVB, purpose unknown { ptr=scanlong(ptr,&l); printf("unknownbit01=%ld ",l); } if(bits&0x0002) { ptr=scanint(ptr,&i); printf("topspacing=%d ",i); } if(bits&0x0004) { ptr=scanint(ptr,&i); printf("bottomspacing=%d ",i); } if(bits&0x0008) { ptr=scanint(ptr,&i); printf("linespacing=%d ",i); } if(bits&0x0010) { ptr=scanint(ptr,&i); printf("leftindent=%d ",i); } if(bits&0x0020) { ptr=scanint(ptr,&i); printf("rightindent=%d ",i); } if(bits&0x0040) { ptr=scanint(ptr,&i); printf("firstlineindent=%d ",i); } if(bits&0x0080) printf("unknownbit80set ",i); if(bits&0x0100) { b=(unsigned char)*ptr++; if(b&1) printf("box "); if(b&2) printf("topborder "); if(b&4) printf("leftborder "); if(b&8) printf("bottomborder "); if(b&0x10) printf("rightborder "); if(b&0x20) printf("thickborder "); if(b&0x40) printf("doubleborder "); if(b&0x80) printf("unknownborder "); printf("%04x ",*(unsigned short *)ptr); ptr+=2; } if(bits&0x0200) { ptr=scanint(ptr,&i); printf("tabs=%d ",i); while(i-->0) { ptr=scanword(ptr,&w); printf("stop=%d ",w&0x3FFF); if(w&0x4000) { ptr=scanword(ptr,&w); if(w==1) { printf("right "); } else if(w==2) { printf("center "); } else { printf("unknowntabmodifier=%02x ",w); } } } } if(bits&0x0400) printf("rightalign "); if(bits&0x0800) printf("centeralign "); if(bits&0x1000) printf("keeplinestogether "); if(bits&0x2000) printf("unknownbit2000set "); // found in PRINTMAN.HLP if(bits&0x4000) printf("unknownbit4000set "); // found in PRINTMAN.HLP if(bits&0x8000) printf("unknownbit8000set "); printf("\n"); while(1) { while(*str) { if(isprint((unsigned char)*str)) { putchar(*str++); } else { printf("(%02x)",*(unsigned char *)str++); } } str++; if((unsigned char)ptr[0]==0xFF) { ptr++; break; } else switch((unsigned char)ptr[0]) { case 0x20: printf("{vfld%ld}",*(long *)(ptr+1)); ptr+=5; break; case 0x21: printf("{dtype%d}",*(short *)(ptr+1)); ptr+=3; break; case 0x80: // font change printf("[font=%u]",*(short *)(ptr+1)); ptr+=3; break; case 0x81: printf("[LF]\n"); ptr++; break; case 0x82: printf("[CR]\n"); ptr++; break; case 0x83: printf("[TAB]"); ptr++; break; case 0x86: ptr++; b=*ptr++; if(b==0x05) cmd="ewc"; else cmd="bmc"; goto picture; case 0x87: ptr++; b=*ptr++; if(b==0x05) cmd="ewl"; else cmd="bml"; goto picture; case 0x88: ptr++; b=*ptr++; if(b==0x05) cmd="ewr"; else cmd="bmr"; goto picture; picture: printf("[%s %02x ",cmd,b); ptr=scanlong(ptr,&l); switch(b) { case 0x22: // HC31 ptr=scanword(ptr,&w); printf("hotspots=%u ",w); case 0x03: // HC30 switch(*(unsigned short *)ptr) { case 0: printf("baggage "); break; case 1: printf("embedded "); break; default: printf("%04x ",((unsigned short *)ptr)[0]); } printf("bm%u]",((unsigned short *)ptr)[1]); break; case 0x05: printf("%04x ",((unsigned short *)ptr)[0]); printf("%04x ",((unsigned short *)ptr)[1]); printf("%04x ",((unsigned short *)ptr)[2]); printf("%s]",ptr+6); break; default: error("Unknown picture flags %02x\n",b); } ptr+=l; break; case 0x89: // end of hot spot printf("[U]"); ptr++; break; case 0xC8: // macro printf("[!%s]",ptr+3); ptr+=*(short *)(ptr+1)+3; break; case 0xCC: // macro without font change printf("[*!%s]",ptr+3); ptr+=*(short *)(ptr+1)+3; break; case 0xE0: // Popup HC30 printf("[^TOPIC%ld]",*(long *)(ptr+1)); ptr+=5; break; case 0xE1: // Jump HC30 printf("[TOPIC%ld]",*(long *)(ptr+1)); ptr+=5; break; case 0xE2: // Popup HC31 printf("[^%08lx]",*(long *)(ptr+1)); ptr+=5; break; case 0xE3: // Jump HC31 printf("[%08lx]",*(long *)(ptr+1)); ptr+=5; break; case 0xE6: // Popup without font change printf("[*^%08lx]",*(long *)(ptr+1)); ptr+=5; break; case 0xE7: // Jump without font change printf("[*%08lx]",*(long *)(ptr+1)); ptr+=5; break; case 0xEA: // Popup into external file / secondary window cmd="^"; goto jump; case 0xEB: // Jump into external file / secondary window cmd=""; goto jump; case 0xEE: // Popup into external file / secondary window without font change cmd="^*"; goto jump; case 0xEF: // Jump into external file / secondary window without font change cmd="*"; goto jump; jump: switch(ptr[3]) { case 0: printf("[%s%08lx] ",cmd,*(long *)(ptr+4)); break; case 1: // Popup into secondary window (silly) printf("[%s%08lx>%d]",cmd,*(long *)(ptr+4),(unsigned char)ptr[8]); break; case 4: printf("[%s%08lx@%s] ",cmd,*(long *)(ptr+4),ptr+8); break; case 6: // Popup into external file / secondary window (silly) printf("[%s%08lx>%s@%s] ",cmd,*(long *)(ptr+4),ptr+8,strchr(ptr+8,'\0')+1); break; default: printf("["); for(i=0;i<*(short *)(ptr+1);i++) printf("%02x",(unsigned char)ptr[i]); printf("]"); } ptr+=*(short *)(ptr+1)+3; break; default: printf("[%02x]",(unsigned char)*ptr++); } } printf("\n"); } free(LinkData2); LinkData2=NULL; } if(LinkData2) { if(TopicLink.DataLen2<=TopicLink.BlockSize-TopicLink.DataLen1) { char *ptr; char *end; end=LinkData2+TopicLink.DataLen2; for(ptr=LinkData2;ptr<end;ptr++) { if(isprint((unsigned char)*ptr)) { putchar(*ptr); } else { printf("(%02x)",*(unsigned char *)ptr); } } } else { StringPrint(LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,NULL); } printf("\n"); } if(LinkData1) free(LinkData1); if(LinkData2) free(LinkData2); memcpy(&TopicLink,&NextTopicLink,sizeof(TopicLink)); } } void HelpDeCompile(FILE *HelpFile,char *dumpfile,int mode) { char buffer[81]; long FileOffset; FILE *rtf; FILE *hpj; myFRead(&HelpHeader,sizeof(HelpHeader),HelpFile); if(HelpHeader.Magic!=0x5F3F) { fprintf(stderr,"Fatal Error: Not a valid WinHelp file !\n"); } else if(!dumpfile) { switch(mode) { case 0: SysLoad(HelpFile); fprintf(stderr,"Decompiling %s...\n",HelpFileTitle); if(before31) { ToMapLoad(HelpFile); } else { ContextLoad(HelpFile); } PhraseLoad(HelpFile); ExportBitmaps(HelpFile); fprintf(stderr,"Pass 1..."); FirstPass(HelpFile); // valid only after ExportBitmaps printf("\n"); _makepath(buffer,NULL,NULL,name,mvp?".MVP":".HPJ"); hpj=myFOpen(buffer,"wt"); _makepath(buffer,NULL,NULL,name,".ICO"); SysList(HelpFile,hpj,buffer); ListBaggage(HelpFile,hpj); AliasList(hpj); // after ContextLoad, before TopicDump _makepath(buffer,NULL,NULL,name,".PH"); PhraseList(buffer); // after PhraseLoad _makepath(buffer,NULL,NULL,name,".RTF"); rtf=myFOpen(buffer,"wt"); fprintf(hpj,"[FILES]\n%s\n\n",buffer); fprintf(rtf,"{\\rtf1\\ansi \\deff0\n"); FontLoad(HelpFile,rtf); fprintf(rtf,"{\\stylesheet{\\fs20\\lang1031 \\snext0 Normal;}}{\\info{\\creatim\\yr1995\\mo2\\dy22\\hr2\\min22}{\\revtim\\yr1995\\mo2\\dy26\\hr22\\min24}{\\version9}{\\edmins0}{\\nofpages0}{\\nofwords0}{\\nofchars0}{\\vern16433}}\n" "\\paperw11906\\paperh16838\\margl1417\\margr1417\\margt1417\\margb1134\\gutter0 \\deftab709\\widowctrl\\ftnbj\\hyphhotz425 \\sectd \\linex0\\headery709\\footery709\\colsx709\\endnhere \\pard\\plain \\fs20\\lang1031\n"); printf("Pass 2..."); TopicDump(HelpFile,rtf,FALSE); fprintf(stderr,"\n"); NotInAnyTopic=FALSE; CTXOMAPList(HelpFile,hpj); fprintf(rtf,"}"); myFClose(rtf); if(extensions&&strcmp(helpcomp,"HCW")!=0) ListBitmaps(hpj); myFClose(hpj); if(Offsets) printf("Help Compiler will issue Warning 5098: Using old key-phrase table\n"); if(missing) printf("Help Compiler will issue Error 1230: File 'missing.bmp' not found\n"); if(NotInAnyTopic) printf("Help Compiler will issue Warning 4098: Context string(s) in [MAP] section not defined in any topic\n"); if(!extractmacros) printf("Help Compiler may issue Warning 4131: Hash conflict between 'x' and 'y'.\n"); if(warnings) { _makepath(buffer,drive,dir,name,ext); printf("HELPDECO had problems with %s. Rebuilt helpfile may behave bad.\n",buffer); } if(helpcomp[0]) { _makepath(buffer,NULL,NULL,name,mvp?".MVP":".HPJ"); printf("Use %s %s to recompile helpfile.\n",helpcomp,buffer); } break; case 1: HexDump(HelpFile,HelpHeader.DirectoryStart); break; case 2: ListFiles(HelpFile); break; case 3: SysLoad(HelpFile); ExportBitmaps(HelpFile); PhraseLoad(HelpFile); _makepath(buffer,NULL,NULL,name,".RTF"); rtf=myFOpen(buffer,"wt"); fprintf(rtf,"{\\rtf1\\ansi \\deff0\n"); FontLoad(HelpFile,rtf); fprintf(rtf,"{\\stylesheet{\\fs20\\lang1031 \\snext0 Normal;}}{\\info{\\creatim\\yr1995\\mo2\\dy22\\hr2\\min22}{\\revtim\\yr1995\\mo2\\dy26\\hr22\\min24}{\\version9}{\\edmins0}{\\nofpages0}{\\nofwords0}{\\nofchars0}{\\vern16433}}\n" "\\paperw11906\\paperh16838\\margl1417\\margr1417\\margt1417\\margb1134\\gutter0 \\deftab709\\widowctrl\\ftnbj\\hyphhotz425 \\sectd \\linex0\\headery709\\footery709\\colsx709\\endnhere \\pard\\plain \\fs20\\lang1031\n"); TopicDump(HelpFile,rtf,TRUE); fprintf(rtf,"}"); myFClose(rtf); break; case 4: SysLoad(HelpFile); _makepath(buffer,NULL,NULL,name,".CNT"); rtf=myFOpen(buffer,"wt"); GenerateContent(HelpFile,rtf); myFClose(rtf); break; case 5: SysLoad(HelpFile); ListTopic(HelpFile); break; case 6: // check external references SysLoad(HelpFile); fprintf(stderr,"Checking %s...\n",HelpFileTitle); PhraseLoad(HelpFile); ExportBitmaps(HelpFile); FirstPass(HelpFile); // valid only after ExportBitmaps printf("\n"); break; } } else { if(!SearchFile(HelpFile,dumpfile,&FileOffset)) { buffer[0]='|'; strcpy(buffer+1,dumpfile); if(!SearchFile(HelpFile,buffer,&FileOffset)) { fprintf(stderr,"Internal file %s not found.\n",dumpfile); return; } dumpfile=buffer; } if(mode) { HexDump(HelpFile,FileOffset); } else if(strcmp(dumpfile,"|Phrases")==0||strcmp(dumpfile,"|PhrIndex")==0) { SysLoad(HelpFile); PhraseLoad(HelpFile); PhraseDump(); } else if(strcmp(dumpfile,"|SYSTEM")==0) { SysDump(HelpFile,FileOffset); } else if(strcmp(dumpfile,"|FONT")==0) { FontDump(HelpFile,FileOffset); } else if(strcmp(dumpfile,"|TOMAP")==0) { ToMapDump(HelpFile,FileOffset); } else if(strcmp(dumpfile,"|TOPIC")==0) { SysLoad(HelpFile); PhraseLoad(HelpFile); DumpTopic(HelpFile,FileOffset); } else if(strcmp(dumpfile,"|TTLBTREE")==0) { TTLDump(HelpFile,FileOffset); } else if(strcmp(dumpfile,"|Rose")==0) { RoseDump(HelpFile,FileOffset); } else if(strcmp(dumpfile,"|TopicId")==0) { TopicIdDump(HelpFile,FileOffset); } else if(strcmp(dumpfile,"|VIOLA")==0) { VIOLADump(HelpFile,FileOffset); } else if(strcmp(dumpfile,"|Petra")==0) { PetraDump(HelpFile,FileOffset); } else if(dumpfile[0]=='|'&&strcmp(dumpfile+2,"WMAP")==0||strcmp(dumpfile+2,"KWMAP")==0) { KWMapDump(HelpFile,FileOffset); } else if(dumpfile[0]=='|'&&strcmp(dumpfile+2,"WDATA")==0||strcmp(dumpfile+2,"KWDATA")==0) { KWDataDump(HelpFile,FileOffset); } else if(dumpfile[0]=='|'&&strcmp(dumpfile+2,"WBTREE")==0||strcmp(dumpfile+2,"KWBTREE")==0) { KWBTreeDump(HelpFile,FileOffset); } else if(strcmp(dumpfile,"|CATALOG")==0) { CatalogDump(HelpFile,FileOffset); } else if(strcmp(dumpfile,"|CONTEXT")==0) { ContextDump(HelpFile,FileOffset); } else if(strcmp(dumpfile,"|CTXOMAP")==0) { CTXOMAPDump(HelpFile,FileOffset); } else if(strcmp(dumpfile,"|PhrImage")==0) { PhrImageDump(HelpFile,FileOffset); } else { HexDump(HelpFile,FileOffset); } } } int main(int argc,char *argv[]) { char buffer[81]; FILE *f; int mode; char *filename; char *dumpfile; int i; memset(table,0,sizeof(table)); for(i=0;i<9;i++) table['1'+i]=i+1; table['0']=10; table['.']=12; table['_']=13; for(i=0;i<26;i++) table['A'+i]=table['a'+i]=17+i; dumpfile=NULL; filename=NULL; mode=0; for(i=1;i<argc;i++) { if(argv[i][0]=='/'||argv[i][0]=='-') switch(tolower((unsigned char)argv[i][1])) { case 'p': mode=6; extractmacros=TRUE; resolvebrowse=FALSE; checkexternal=TRUE; break; case 'y': overwrite=TRUE; break; case 'c': mode=4; break; case 'x': mode=1; break; case 'd': mode=2; break; case 'r': mode=3; break; case 'w': warn=TRUE; break; case 't': mode=5; break; case 'm': extractmacros=FALSE; break; case 'b': resolvebrowse=FALSE; break; default: fprintf(stderr,"unknown option '%s' ignored\n",argv[i]); } else if(dumpfile) { fprintf(stderr,"additional parameter '%s' ignored\n",argv[i]); } else if(filename) { dumpfile=argv[i]; } else { filename=argv[i]; } } if(filename) { strupr(filename); _splitpath(filename,drive,dir,name,ext); if(ext[0]=='\0') strcpy(ext,".HLP"); mvp=ext[1]=='M'; _makepath(buffer,drive,dir,name,ext); f=fopen(buffer,"rb"); if(f) { if(((MFILE *)f)->magic==MAGIC) { fprintf(stderr,"Error opening %s\n",buffer); } else { HelpDeCompile(f,dumpfile,mode); } myFClose(f); } else { fprintf(stderr,"Can not open '%s'\n",buffer); } } else { fprintf(stderr,"HELPDECO - decompile *.HLP/*.MVB files of Windows 3.0/3.1/3.11/95 - Version 1.8\n" "M.Winterhoff, Geschw.-Scholl-Ring 17, 38444 Wolfsburg, Germany, CIS 100326,2776\n" "\n" "usage: HELPDECO helpfile[.hlp] - decompile helpfile into all sources\n" " HELPDECO helpfile[.hlp] /r - decompile into lookalike RTF\n" " HELPDECO helpfile[.hlp] /c - generate a content (*.CNT) file\n" " HELPDECO helpfile[.hlp] /p - check external references\n" " HELPDECO helpfile[.hlp] /d - display internal directory\n" " HELPDECO helpfile[.hlp] /x - HexDump of internal directory\n" " HELPDECO helpfile[.hlp] \"internalfile\" - displays internal file\n" " HELPDECO helpfile[.hlp] \"internalfile\" /x - HexDump of internal file\n" "option: /y - overwrite without asking\n" "\n" "This program is public domain. Use at your own risk. No part of it may be used\n" "commercially. No fees may be charged on copying. Always distribute with source.\n" "\n" "To recreate all source files neccessary to rebuild a Windows helpfile, create\n" "a directory, change to this directory and call HELPDECO with the path and name\n" "of the helpfile to dissect. HELPDECO will extract all files contained in the\n" "helpfile in two passes and deposit them in the current directory. You may then\n" "rebuild the helpfile using the appropriate help compiler HC30, HC31, HCP, HCW,\n" "MVC, or WMVC. The file is not binary compatible, but should look and work like\n" "the original. HELPDECO cannot handle certain large helpfiles.\n"); } return 0; }